Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions app/components/form/fields/NameField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ export function NameField<
required={required}
label={label}
name={name}
transform={(value) =>
value
.toLowerCase()
.replace(/[\s_]+/g, '-')
.replace(/[^a-z0-9-]/g, '')
}
{...textFieldProps}
/>
)
Expand Down
2 changes: 1 addition & 1 deletion app/components/form/fields/TextField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export interface TextFieldProps<
validate?: Validate<FieldPathValue<TFieldValues, TName>, TFieldValues>
control: Control<TFieldValues>
/** Alters the value of the input during the field's onChange event. */
transform?: (value: string) => FieldPathValue<TFieldValues, TName>
transform?: (value: string) => string
}

export function TextField<
Expand Down
14 changes: 5 additions & 9 deletions app/forms/network-interface-create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/
import { useMemo } from 'react'
import { useForm } from 'react-hook-form'
import type { SetRequired } from 'type-fest'

import { useApiQuery, type ApiError, type InstanceNetworkInterfaceCreate } from '@oxide/api'

Expand All @@ -19,10 +20,10 @@ import { SideModalForm } from '~/components/form/SideModalForm'
import { useProjectSelector } from '~/hooks/use-params'
import { FormDivider } from '~/ui/lib/Divider'

const defaultValues: InstanceNetworkInterfaceCreate = {
const defaultValues: SetRequired<InstanceNetworkInterfaceCreate, 'ip'> = {
name: '',
description: '',
ip: undefined,
ip: '',
subnetName: '',
vpcName: '',
}
Expand Down Expand Up @@ -58,7 +59,7 @@ export function CreateNetworkInterfaceForm({
resourceName="network interface"
title="Add network interface"
onDismiss={onDismiss}
onSubmit={onSubmit}
onSubmit={({ ip, ...rest }) => onSubmit({ ip: ip.trim() || undefined, ...rest })}
loading={loading}
submitError={submitError}
>
Expand All @@ -81,12 +82,7 @@ export function CreateNetworkInterfaceForm({
required
control={form.control}
/>
<TextField
name="ip"
label="IP Address"
control={form.control}
transform={(ip) => (ip.trim() === '' ? undefined : ip)}
/>
<TextField name="ip" label="IP Address" control={form.control} />
</SideModalForm>
)
}
8 changes: 5 additions & 3 deletions test/e2e/project-create.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,15 @@ test.describe('Project create', () => {
})

test('shows field-level validation error and does not POST', async ({ page }) => {
await page.fill('role=textbox[name="Name"]', 'Invalid name')

const input = page.getByRole('textbox', { name: 'Name' })
await input.pressSequentially('no sPoNgEbOb_CaSe or spaces')
await expect(input).toHaveValue('no-spongebob-case-or-spaces')
await input.fill('no-ending-dash-')
// submit to trigger validation
await page.getByRole('button', { name: 'Create project' }).click()

await expect(
page.getByText('Can only contain lower-case letters, numbers, and dashes').nth(0)
page.getByText('Must end with a letter or number', { exact: true }).nth(0)
).toBeVisible()
})

Expand Down