Skip to content

Commit 0448f9a

Browse files
committed
fix: improve typing of JSFormDialog
Allow open and onOpenChange prop to be more flexible and work the same as the Dialog.
1 parent ac37ce9 commit 0448f9a

File tree

6 files changed

+131
-20
lines changed

6 files changed

+131
-20
lines changed

package-lock.json

Lines changed: 27 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
},
4141
"peerDependencies": {
4242
"@committed/components": "^8.0.0",
43+
"@committed/hooks": "^0.10.4",
4344
"react": "^16.8.0 || ^17.0.0"
4445
},
4546
"prettier": {
@@ -83,9 +84,10 @@
8384
]
8485
},
8586
"devDependencies": {
86-
"@committed/components": "^8.2.1",
8787
"@commitlint/cli": "^16.2.3",
8888
"@commitlint/config-conventional": "^16.2.1",
89+
"@committed/components": "^8.2.1",
90+
"@committed/hooks": "^0.10.4",
8991
"@fontsource/dosis": "^4.5.7",
9092
"@fontsource/inter": "^4.5.7",
9193
"@sinclair/typebox": "0.22.1",

src/components/JSForm/JSForm.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
ObjectFieldTemplate,
1818
} from '../templates'
1919
import { Widgets } from '../widgets'
20+
2021
const { getDefaultRegistry } = utils
2122
const { fields, widgets } = getDefaultRegistry()
2223

src/components/JSFormDialog/JSFormDialog.stories.tsx

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
Button,
3+
Checkbox,
34
Dialog,
45
DialogContent,
56
DialogTitle,
@@ -10,7 +11,7 @@ import { ISubmitEvent } from '@rjsf/core'
1011
import { Type } from '@sinclair/typebox'
1112
import { Meta, Story } from '@storybook/react'
1213
import { JSONSchema7 } from 'json-schema'
13-
import React from 'react'
14+
import React, { useState } from 'react'
1415
import { JSFormDialog, JSFormDialogProps } from '.'
1516

1617
export default {
@@ -130,7 +131,7 @@ type MyFormData = {
130131

131132
export const Typed: Story = () => {
132133
const handleSubmit = (e: ISubmitEvent<MyFormData>) => {
133-
alert(e.formData)
134+
alert(JSON.stringify(e.formData, null, 2))
134135
}
135136

136137
return (
@@ -143,7 +144,7 @@ export const Typed: Story = () => {
143144
properties: {
144145
first: {
145146
type: 'string',
146-
title: 'Firstname',
147+
title: 'First name',
147148
},
148149
second: {
149150
type: 'string',
@@ -158,3 +159,75 @@ export const Typed: Story = () => {
158159
</div>
159160
)
160161
}
162+
163+
export const Controlled: Story = () => {
164+
const [open, setOpen] = useState(false)
165+
166+
const handleSubmit = (e: ISubmitEvent<MyFormData>) => {
167+
alert(JSON.stringify(e.formData, null, 2))
168+
}
169+
170+
return (
171+
<div>
172+
<Checkbox label="Open" checked={open} onCheckedChange={setOpen} />
173+
<JSFormDialog<MyFormData>
174+
open={open}
175+
onOpenChange={setOpen}
176+
schema={{
177+
title: 'name',
178+
type: 'object',
179+
required: ['first', 'second'],
180+
properties: {
181+
first: {
182+
type: 'string',
183+
title: 'Firstname',
184+
},
185+
second: {
186+
type: 'string',
187+
title: 'Surname',
188+
},
189+
},
190+
}}
191+
onSubmit={handleSubmit}
192+
/>
193+
</div>
194+
)
195+
}
196+
197+
export const SetControlled: Story = () => {
198+
const [open, setOpen] = useState(false)
199+
200+
const handleSubmit = (e: ISubmitEvent<MyFormData>) => {
201+
alert(JSON.stringify(e.formData, null, 2))
202+
}
203+
204+
return (
205+
<div>
206+
<Checkbox
207+
label="Open"
208+
checked={open}
209+
onCheckedChange={(value: boolean) => setOpen(value)}
210+
/>
211+
<JSFormDialog<MyFormData>
212+
open={open}
213+
onOpenChange={setOpen}
214+
schema={{
215+
title: 'name',
216+
type: 'object',
217+
required: ['first', 'second'],
218+
properties: {
219+
first: {
220+
type: 'string',
221+
title: 'Firstname',
222+
},
223+
second: {
224+
type: 'string',
225+
title: 'Surname',
226+
},
227+
},
228+
}}
229+
onSubmit={handleSubmit}
230+
/>
231+
</div>
232+
)
233+
}

src/components/JSFormDialog/JSFormDialog.test.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ import { Default } from './JSFormDialog.stories'
66

77
it('renders and can submit', () => {
88
const onSubmit = jest.fn()
9+
const setOpen = jest.fn()
910
const { asFragment } = renderLight(
1011
<Default
11-
dialog={{ open: true }}
12+
open={true}
13+
onOpenChange={setOpen}
1214
schema={Type.String()}
1315
onSubmit={onSubmit}
1416
/>
@@ -22,9 +24,11 @@ it('renders and can submit', () => {
2224

2325
it('renders and can cancel', () => {
2426
const onSubmit = jest.fn()
27+
const setOpen = jest.fn()
2528
renderLight(
2629
<Default
27-
dialog={{ open: true }}
30+
open={true}
31+
onOpenChange={setOpen}
2832
schema={Type.String()}
2933
onSubmit={onSubmit}
3034
/>

src/components/JSFormDialog/JSFormDialog.tsx

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,23 @@ import {
66
DialogTrigger,
77
Inline,
88
} from '@committed/components'
9-
import React, { ComponentProps, FormEvent, useCallback, useState } from 'react'
9+
import { useControllableState } from '@committed/hooks'
10+
import React, { ComponentProps, FormEvent, useCallback } from 'react'
1011
import { generateForm, JSFormProps, JSFormSubmit } from '../JSForm'
1112

12-
export type JSFormDialogProps<T> = JSFormProps<T> & {
13-
dialog?: ComponentProps<typeof Dialog>
14-
css?: CSS
15-
cancelText?: string
16-
submitText?: string
17-
}
13+
export type JSFormDialogProps<T> = JSFormProps<T> &
14+
ComponentProps<typeof Dialog> & {
15+
css?: CSS
16+
cancelText?: string
17+
submitText?: string
18+
}
1819

1920
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2021
export function JSFormDialog<T = any>({
21-
dialog,
22+
open: controlledOpen,
23+
onOpenChange: controlledOnOpenChange,
24+
modal = true,
25+
defaultOpen = false,
2226
css,
2327
Form = generateForm<T>(),
2428
cancelText = 'Cancel',
@@ -27,7 +31,11 @@ export function JSFormDialog<T = any>({
2731
onSubmit,
2832
...props
2933
}: JSFormDialogProps<T>) {
30-
const [open, setOpen] = useState(false)
34+
const [open, setOpen] = useControllableState(
35+
controlledOpen,
36+
controlledOnOpenChange,
37+
defaultOpen
38+
)
3139

3240
const handleSubmit = useCallback(
3341
(e: JSFormSubmit<T>, n: FormEvent<HTMLFormElement>) => {
@@ -42,7 +50,7 @@ export function JSFormDialog<T = any>({
4250
const handleClose = useCallback(() => setOpen(false), [setOpen])
4351

4452
return (
45-
<Dialog modal={true} open={open} {...dialog}>
53+
<Dialog modal={modal} open={open} onOpenChange={setOpen}>
4654
<DialogTrigger onClick={handleOpen}>{children}</DialogTrigger>
4755
<DialogContent css={css as CSS} defaultClose={false}>
4856
<Form {...props} onSubmit={handleSubmit}>

0 commit comments

Comments
 (0)