Skip to content
Open
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
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@

- Reach for primitives in `app/ui` before inventing page-specific widgets; that directory holds router-agnostic building blocks.
- When you just need Tailwind classes on a DOM element, use the `classed` helper instead of creating one-off wrappers (`app/util/classed.ts`).
- Define helper components at the module level, not inside other components' render functions—the `react/no-unstable-nested-components` eslint rule enforces this to prevent performance issues and broken component identity. Extract nested components to the top level and pass any needed values as props.
- Reuse utility components for consistent formatting—`TimeAgo`, `EmptyMessage`, `CardBlock`, `DocsPopover`, `PropertiesTable`, etc.
- Import icons from `@oxide/design-system/icons/react` with size suffixes: `16` for inline/table, `24` for headers/buttons, `12` for tiny indicators.
- Keep help URLs in `links`/`docLinks` (`app/util/links.ts`).
Expand Down
11 changes: 10 additions & 1 deletion app/components/form/fields/IpPoolSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ type IpPoolSelectorProps<
/** Compatible IP versions based on network interface type */
compatibleVersions?: IpVersion[]
required?: boolean
hideOptionalTag?: boolean
label?: string
/** Hide visible label, using it as aria-label instead */
hideLabel?: boolean
}

export function IpPoolSelector<
Expand All @@ -67,6 +71,9 @@ export function IpPoolSelector<
disabled = false,
compatibleVersions = ALL_IP_VERSIONS,
required = true,
hideOptionalTag = false,
label = 'Pool',
hideLabel = false,
}: IpPoolSelectorProps<TFieldValues, TName>) {
// Note: pools are already filtered by poolType before being passed to this component
const sortedPools = useMemo(() => {
Expand All @@ -84,12 +91,14 @@ export function IpPoolSelector<
<ListboxField
name={poolFieldName}
items={sortedPools.map(toIpPoolItem)}
label="Pool"
label={label}
hideLabel={hideLabel}
noItemsPlaceholder="No pools available"
control={control}
placeholder="Select a pool"
required={required}
disabled={disabled}
hideOptionalTag={hideOptionalTag}
/>
</div>
)
Expand Down
4 changes: 4 additions & 0 deletions app/components/form/fields/ListboxField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export type ListboxFieldProps<
placeholder?: string
className?: string
label?: string
/** Hide visible label, using it as aria-label instead */
hideLabel?: boolean
required?: boolean
description?: string | React.ReactNode
control: Control<TFieldValues>
Expand Down Expand Up @@ -54,6 +56,7 @@ export function ListboxField<
isLoading,
noItemsPlaceholder,
hideOptionalTag,
hideLabel,
}: ListboxFieldProps<TFieldValues, TName>) {
// TODO: recreate this logic
// validate: (v) => (required && !v ? `${name} is required` : undefined),
Expand All @@ -63,6 +66,7 @@ export function ListboxField<
<Listbox
description={description}
label={label}
hideLabel={hideLabel}
required={required}
placeholder={placeholder}
noItemsPlaceholder={noItemsPlaceholder}
Expand Down
Loading
Loading