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
4 changes: 3 additions & 1 deletion app/components/form/fields/ComboboxField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
getSelectedLabelFromValue,
type ComboboxBaseProps,
} from '~/ui/lib/Combobox'
import { capitalize } from '~/util/str'
import { capitalize, normalizeName } from '~/util/str'

import { ErrorMessage } from './ErrorMessage'

Expand Down Expand Up @@ -60,6 +60,7 @@ export function ComboboxField<
? 'Select an option or enter a custom value'
: 'Select an option',
items,
transform = (value) => normalizeName(value, true),
validate,
...props
}: ComboboxFieldProps<TFieldValues, TName>) {
Expand Down Expand Up @@ -89,6 +90,7 @@ export function ComboboxField<
}}
allowArbitraryValues={allowArbitraryValues}
inputRef={field.ref}
transform={transform}
{...props}
/>
<ErrorMessage error={fieldState.error} label={label} />
Expand Down
26 changes: 19 additions & 7 deletions app/ui/lib/Combobox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ export type ComboboxBaseProps = {
onInputChange?: (value: string) => void
/** Fires whenever the Enter key is pressed while Combobox input has focus */
onEnter?: (event: React.KeyboardEvent<HTMLInputElement>) => void
/**
* Optional function to transform the value entered into the input as the user types.
* Defaults in ComboboxField to running the `normalizeName` function on the input value.
*/
transform?: (value: string) => string
}

type ComboboxProps = {
Expand Down Expand Up @@ -93,6 +98,7 @@ export const Combobox = ({
allowArbitraryValues = false,
hideOptionalTag,
inputRef,
transform,
...props
}: ComboboxProps) => {
const [query, setQuery] = useState(selectedItemValue || '')
Expand Down Expand Up @@ -189,17 +195,23 @@ export const Combobox = ({
>
<ComboboxInput
id={`${id}-input`}
// displayValue controls what's displayed in the input field.
// selectedItemValue is displayed when the user can type in a new value.
// Otherwise, use the provided selectedItemLabel
displayValue={() =>
allowArbitraryValues ? selectedItemValue : selectedItemLabel
// If an option has been selected, display either the selected item's label or value.
// If no option has been selected yet, or the user has started editing the input, display the query.
// We are using value here, as opposed to Headless UI's displayValue, so we can normalize
// the value entered into the input (via the onChange event).
value={
selectedItemValue
? allowArbitraryValues
? selectedItemValue
: selectedItemLabel
: query
}
onChange={(event) => {
const value = transform ? transform(event.target.value) : event.target.value
// updates the query state as the user types, in order to filter the list of items
setQuery(event.target.value)
setQuery(value)
// if the parent component wants to know about input changes, call the callback
onInputChange?.(event.target.value)
onInputChange?.(value)
}}
onKeyDown={(e) => {
// Prevent form submission when the user presses Enter inside a combobox.
Expand Down
6 changes: 4 additions & 2 deletions test/e2e/firewall-rules.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,11 +193,13 @@ test('firewall rule form targets table', async ({ page }) => {

// now add a subnet by entering text
await selectOption(page, 'Target type', 'VPC subnet')
await subnetNameField.fill('abc')
// test that the name typed in is normalized
await subnetNameField.fill('ABC 123')
await expect(subnetNameField).toHaveValue('abc-123')
// hit enter to submit the subform
await subnetNameField.press('Enter')
await subnetNameField.press('Enter')
await expectRowVisible(targets, { Type: 'subnet', Value: 'abc' })
await expectRowVisible(targets, { Type: 'subnet', Value: 'abc-123' })

// add IP target
await selectOption(page, 'Target type', 'IP')
Expand Down
Loading