-
Notifications
You must be signed in to change notification settings - Fork 18
Attach disk modal + ModalForm
#2746
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b4ce08d
036b295
5bdb549
a0e2b0c
5824157
13ac403
f591a2b
1cecbcc
1b568f9
aa7ecdb
f17a1ac
e918849
39ac93a
5ca2552
163aae3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| /* | ||
| * This Source Code Form is subject to the terms of the Mozilla Public | ||
| * License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| * file, you can obtain one at https://mozilla.org/MPL/2.0/. | ||
| * | ||
| * Copyright Oxide Computer Company | ||
| */ | ||
|
|
||
| import { useId, type ReactNode } from 'react' | ||
| import type { FieldValues, UseFormReturn } from 'react-hook-form' | ||
|
|
||
| import type { ApiError } from '@oxide/api' | ||
|
|
||
| import { Message } from '~/ui/lib/Message' | ||
| import { Modal, type ModalProps } from '~/ui/lib/Modal' | ||
|
|
||
| type ModalFormProps<TFieldValues extends FieldValues> = { | ||
| form: UseFormReturn<TFieldValues> | ||
| children: ReactNode | ||
|
|
||
| /** Must be provided with a reason describing why it's disabled */ | ||
| submitDisabled?: boolean | ||
| onSubmit: (values: TFieldValues) => void | ||
| submitLabel: string | ||
|
|
||
| // require loading and error so we can't forget to hook them up. there are a | ||
| // few forms that don't need them, so we'll use dummy values | ||
|
|
||
| /** Error from the API call */ | ||
| submitError: ApiError | null | ||
| loading: boolean | ||
| } & Omit<ModalProps, 'isOpen'> | ||
|
|
||
| export function ModalForm<TFieldValues extends FieldValues>({ | ||
| form, | ||
| children, | ||
| onDismiss, | ||
| submitDisabled = false, | ||
| submitError, | ||
| title, | ||
| onSubmit, | ||
| submitLabel = 'Save', | ||
| loading, | ||
| width = 'medium', | ||
| overlay = true, | ||
| }: ModalFormProps<TFieldValues>) { | ||
| const id = useId() | ||
| const { isSubmitting } = form.formState | ||
|
|
||
| return ( | ||
| <Modal isOpen onDismiss={onDismiss} title={title} width={width} overlay={overlay}> | ||
| <Modal.Body> | ||
| <Modal.Section> | ||
| {submitError && ( | ||
| <Message variant="error" title="Error" content={submitError.message} /> | ||
| )} | ||
| <form | ||
| id={id} | ||
| className="ox-form" | ||
| autoComplete="off" | ||
| onSubmit={(e) => { | ||
| if (!onSubmit) return | ||
| // This modal being in a portal doesn't prevent the submit event | ||
| // from bubbling up out of the portal. Normally that's not a | ||
| // problem, but sometimes (e.g., instance create) we render the | ||
| // SideModalForm from inside another form, in which case submitting | ||
| // the inner form submits the outer form unless we stop propagation | ||
| e.stopPropagation() | ||
| form.handleSubmit(onSubmit)(e) | ||
| }} | ||
| > | ||
| {children} | ||
| </form> | ||
| </Modal.Section> | ||
| </Modal.Body> | ||
| <Modal.Footer | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On the |
||
| onDismiss={onDismiss} | ||
| formId={id} | ||
| actionText={submitLabel} | ||
| disabled={submitDisabled} | ||
| actionLoading={loading || isSubmitting} | ||
| /> | ||
| </Modal> | ||
| ) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,14 +30,6 @@ type EditFormProps = { | |
|
|
||
| type SideModalFormProps<TFieldValues extends FieldValues> = { | ||
| form: UseFormReturn<TFieldValues> | ||
| /** | ||
| * A function that returns the fields. | ||
| * | ||
| * Implemented as a function so we can pass `control` to the fields in the | ||
| * calling code. We could do that internally with `cloneElement` instead, but | ||
| * then in the calling code, the field would not infer `TFieldValues` and | ||
| * constrain the `name` prop to paths in the values object. | ||
| */ | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment has been wrong for two years 😁 |
||
| children: ReactNode | ||
| onDismiss: () => void | ||
| resourceName: string | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to think about our approach to labels here? As in do we use the full version as it is, or just use the verb for. Aka "attach", "create", "save"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think just the verb, otherwise it's too long.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On second thought I like the nouns. I'll try this as
Attach IPto make it shorter.