diff --git a/app/pages/system/silos/SiloIpPoolsTab.tsx b/app/pages/system/silos/SiloIpPoolsTab.tsx
index 7750d816c..7ff0193fc 100644
--- a/app/pages/system/silos/SiloIpPoolsTab.tsx
+++ b/app/pages/system/silos/SiloIpPoolsTab.tsx
@@ -9,7 +9,7 @@
import { useQuery } from '@tanstack/react-query'
import { createColumnHelper } from '@tanstack/react-table'
import { useCallback, useMemo, useState } from 'react'
-import { useForm } from 'react-hook-form'
+import { useForm, useWatch } from 'react-hook-form'
import { type LoaderFunctionArgs } from 'react-router'
import {
@@ -24,6 +24,7 @@ import {
import { Networking24Icon } from '@oxide/design-system/icons/react'
import { Badge } from '@oxide/design-system/ui'
+import { CheckboxField } from '~/components/form/fields/CheckboxField'
import { ComboboxField } from '~/components/form/fields/ComboboxField'
import { HL } from '~/components/HL'
import { IpVersionBadge } from '~/components/IpVersionBadge'
@@ -263,9 +264,10 @@ export const handle = makeCrumb('IP Pools')
type LinkPoolFormValues = {
pool: string | undefined
+ isDefault: boolean
}
-const defaultValues: LinkPoolFormValues = { pool: undefined }
+const defaultValues: LinkPoolFormValues = { pool: undefined, isDefault: false }
function LinkPoolModal({ onDismiss }: { onDismiss: () => void }) {
const { silo } = useSiloSelector()
@@ -282,14 +284,18 @@ function LinkPoolModal({ onDismiss }: { onDismiss: () => void }) {
},
})
- function onSubmit({ pool }: LinkPoolFormValues) {
+ function onSubmit({ pool, isDefault }: LinkPoolFormValues) {
if (!pool) return // can't happen, silo is required
- linkPool.mutate({ path: { pool }, body: { silo, isDefault: false } })
+ linkPool.mutate({ path: { pool }, body: { silo, isDefault } })
}
const allLinkedPools = useQuery(allSiloPoolsQuery(silo).optionsFn())
const allPools = useQuery(allPoolsQuery.optionsFn())
+ // Fetch the selected pool details so we can update the checkbox label.
+ const selectedPoolName = useWatch({ control, name: 'pool' })
+ const selectedPool = allPools.data?.items.find((p) => p.name === selectedPoolName)
+
// in order to get the list of remaining unlinked pools, we have to get the
// list of all pools and remove the already linked ones
@@ -334,6 +340,12 @@ function LinkPoolModal({ onDismiss }: { onDismiss: () => void }) {
required
control={control}
/>
+
+
+ {selectedPool
+ ? `Make default IP${selectedPool.ipVersion} ${selectedPool.poolType} pool for silo`
+ : 'Make default pool for silo'}
+
diff --git a/app/pages/system/silos/SiloSubnetPoolsTab.tsx b/app/pages/system/silos/SiloSubnetPoolsTab.tsx
index 349d42fb4..7820f431f 100644
--- a/app/pages/system/silos/SiloSubnetPoolsTab.tsx
+++ b/app/pages/system/silos/SiloSubnetPoolsTab.tsx
@@ -9,7 +9,7 @@
import { useQuery } from '@tanstack/react-query'
import { createColumnHelper } from '@tanstack/react-table'
import { useCallback, useMemo, useState } from 'react'
-import { useForm } from 'react-hook-form'
+import { useForm, useWatch } from 'react-hook-form'
import { type LoaderFunctionArgs } from 'react-router'
import {
@@ -24,6 +24,7 @@ import {
import { Networking24Icon } from '@oxide/design-system/icons/react'
import { Badge } from '@oxide/design-system/ui'
+import { CheckboxField } from '~/components/form/fields/CheckboxField'
import { ComboboxField } from '~/components/form/fields/ComboboxField'
import { HL } from '~/components/HL'
import { IpVersionBadge } from '~/components/IpVersionBadge'
@@ -252,9 +253,10 @@ export const handle = makeCrumb('Subnet Pools')
type LinkPoolFormValues = {
pool: string | undefined
+ isDefault: boolean
}
-const defaultValues: LinkPoolFormValues = { pool: undefined }
+const defaultValues: LinkPoolFormValues = { pool: undefined, isDefault: false }
function LinkPoolModal({ onDismiss }: { onDismiss: () => void }) {
const { silo } = useSiloSelector()
@@ -271,14 +273,18 @@ function LinkPoolModal({ onDismiss }: { onDismiss: () => void }) {
},
})
- function onSubmit({ pool }: LinkPoolFormValues) {
+ function onSubmit({ pool, isDefault }: LinkPoolFormValues) {
if (!pool) return
- linkPool.mutate({ path: { pool }, body: { silo, isDefault: false } })
+ linkPool.mutate({ path: { pool }, body: { silo, isDefault } })
}
const allLinkedPools = useQuery(allSiloPoolsQuery(silo).optionsFn())
const allPools = useQuery(allPoolsQuery.optionsFn())
+ // Fetch the selected pool details so we can update the checkbox label.
+ const selectedPoolName = useWatch({ control, name: 'pool' })
+ const selectedPool = allPools.data?.items.find((p) => p.name === selectedPoolName)
+
const linkedPoolIds = useMemo(
() =>
allLinkedPools.data ? new Set(allLinkedPools.data.items.map((p) => p.id)) : undefined,
@@ -320,6 +326,12 @@ function LinkPoolModal({ onDismiss }: { onDismiss: () => void }) {
required
control={control}
/>
+
+
+ {selectedPool
+ ? `Make default IP${selectedPool.ipVersion} subnet pool for silo`
+ : 'Make default subnet pool for silo'}
+
diff --git a/test/e2e/silos.e2e.ts b/test/e2e/silos.e2e.ts
index 72192517f..d97afe758 100644
--- a/test/e2e/silos.e2e.ts
+++ b/test/e2e/silos.e2e.ts
@@ -370,14 +370,62 @@ test('Silo IP pools link pool', async ({ page }) => {
await page.getByPlaceholder('Select a pool').fill('x')
await expect(page.getByText('No items match')).toBeVisible()
- // select silo in combobox and click link
+ // before a pool is selected, the default checkbox label is generic
+ await expect(
+ page.getByRole('checkbox', { name: 'Make default pool for silo' })
+ ).toBeVisible()
+
+ // select pool in combobox
await page.getByPlaceholder('Select a pool').fill('ip-pool')
await page.getByRole('option', { name: 'ip-pool-3' }).click()
+
+ // checkbox label now reflects the selected pool's version and type
+ const defaultCheckbox = page.getByRole('checkbox', {
+ name: 'Make default IPv4 unicast pool for silo',
+ })
+ await expect(defaultCheckbox).toBeVisible()
+ await defaultCheckbox.check()
+
+ await modal.getByRole('button', { name: 'Link' }).click()
+
+ // modal closes and we see the pool linked as default in the table
+ await expect(modal).toBeHidden()
+ await expectRowVisible(table, { name: 'ip-pool-3default', Version: 'v4' })
+})
+
+test('Silo subnet pools link pool', async ({ page }) => {
+ await page.goto('/system/silos/maze-war/subnet-pools')
+
+ const table = page.getByRole('table')
+ await expectRowVisible(table, { name: 'default-v4-subnet-pooldefault', Version: 'v4' })
+
+ const modal = page.getByRole('dialog', { name: 'Link pool' })
+ await expect(modal).toBeHidden()
+
+ await page.getByRole('button', { name: 'Link pool' }).click()
+ await expect(modal).toBeVisible()
+
+ // before a pool is selected, the default checkbox label is generic
+ await expect(
+ page.getByRole('checkbox', { name: 'Make default subnet pool for silo' })
+ ).toBeVisible()
+
+ // select pool in combobox
+ await page.getByPlaceholder('Select a pool').fill('myriad')
+ await page.getByRole('option', { name: 'myriad-v4-subnet-pool' }).click()
+
+ // checkbox label now reflects the selected pool's version
+ const defaultCheckbox = page.getByRole('checkbox', {
+ name: 'Make default IPv4 subnet pool for silo',
+ })
+ await expect(defaultCheckbox).toBeVisible()
+ await defaultCheckbox.check()
+
await modal.getByRole('button', { name: 'Link' }).click()
- // modal closes and we see the thing in the table
+ // modal closes and we see the pool linked as default in the table
await expect(modal).toBeHidden()
- await expectRowVisible(table, { name: 'ip-pool-3', Version: 'v4' })
+ await expectRowVisible(table, { name: 'myriad-v4-subnet-pooldefault', Version: 'v4' })
})
// just a convenient form to test this with because it's tall