From ef1c452019c69ef93ef7918aade2caef2a4509ce Mon Sep 17 00:00:00 2001 From: Charlie Park Date: Tue, 22 Oct 2024 15:47:31 -0700 Subject: [PATCH 01/19] Add descriptions to IP Pool dropdowns --- app/forms/floating-ip-create.tsx | 45 ++++++++++++++++++-------------- app/forms/ip-pool-create.tsx | 5 ++++ app/forms/ip-pool-edit.tsx | 5 ++++ 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/app/forms/floating-ip-create.tsx b/app/forms/floating-ip-create.tsx index 77424696c7..ef75c2378e 100644 --- a/app/forms/floating-ip-create.tsx +++ b/app/forms/floating-ip-create.tsx @@ -19,8 +19,8 @@ import { } from '@oxide/api' import { AccordionItem } from '~/components/AccordionItem' +import { ComboboxField } from '~/components/form/fields/ComboboxField' import { DescriptionField } from '~/components/form/fields/DescriptionField' -import { ListboxField } from '~/components/form/fields/ListboxField' import { NameField } from '~/components/form/fields/NameField' import { SideModalForm } from '~/components/form/SideModalForm' import { useProjectSelector } from '~/hooks/use-params' @@ -30,23 +30,28 @@ import { Message } from '~/ui/lib/Message' import { ALL_ISH } from '~/util/consts' import { pb } from '~/util/path-builder' -const toListboxItem = (p: SiloIpPool) => { - if (!p.isDefault) { - return { value: p.name, label: p.name } - } - // For the default pool, add a label to the dropdown - return { - value: p.name, - selectedLabel: p.name, - label: ( - <> - {p.name}{' '} - - default - - - ), - } +const toComboboxItem = (p: SiloIpPool) => { + const value = p.name + const selectedLabel = p.name + const label = ( +
+
+ {p.name} + {p.isDefault && ( + <> + {' '} + + default + + + )} +
+ {p.description.length && ( +
{p.description}
+ )} +
+ ) + return { value, selectedLabel, label } } const defaultValues: Omit = { @@ -106,9 +111,9 @@ export function CreateFloatingIpSideModalForm() { content="If you don’t specify a pool, the default will be used" /> - toListboxItem(p))} + items={(allPools?.items || []).map((p) => toComboboxItem(p))} label="IP pool" control={form.control} placeholder="Select a pool" diff --git a/app/forms/ip-pool-create.tsx b/app/forms/ip-pool-create.tsx index c91e8d8d31..64d864bfbe 100644 --- a/app/forms/ip-pool-create.tsx +++ b/app/forms/ip-pool-create.tsx @@ -14,6 +14,7 @@ import { DescriptionField } from '~/components/form/fields/DescriptionField' import { NameField } from '~/components/form/fields/NameField' import { SideModalForm } from '~/components/form/SideModalForm' import { addToast } from '~/stores/toast' +import { Message } from '~/ui/lib/Message' import { pb } from '~/util/path-builder' const defaultValues: IpPoolCreate = { @@ -51,6 +52,10 @@ export function CreateIpPoolSideModalForm() { > + ) } diff --git a/app/forms/ip-pool-edit.tsx b/app/forms/ip-pool-edit.tsx index 73e2c942c5..71cf051b10 100644 --- a/app/forms/ip-pool-edit.tsx +++ b/app/forms/ip-pool-edit.tsx @@ -20,6 +20,7 @@ import { NameField } from '~/components/form/fields/NameField' import { SideModalForm } from '~/components/form/SideModalForm' import { getIpPoolSelector, useIpPoolSelector } from '~/hooks/use-params' import { addToast } from '~/stores/toast' +import { Message } from '~/ui/lib/Message' import { pb } from '~/util/path-builder' EditIpPoolSideModalForm.loader = async ({ params }: LoaderFunctionArgs) => { @@ -68,6 +69,10 @@ export function EditIpPoolSideModalForm() { > + ) } From 4e93c7acb6c5d243fae5dc367f6525f653156bee Mon Sep 17 00:00:00 2001 From: Charlie Park Date: Tue, 22 Oct 2024 16:08:44 -0700 Subject: [PATCH 02/19] slight type tweak --- app/forms/floating-ip-create.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/forms/floating-ip-create.tsx b/app/forms/floating-ip-create.tsx index ef75c2378e..9f410f8d1e 100644 --- a/app/forms/floating-ip-create.tsx +++ b/app/forms/floating-ip-create.tsx @@ -26,11 +26,12 @@ import { SideModalForm } from '~/components/form/SideModalForm' import { useProjectSelector } from '~/hooks/use-params' import { addToast } from '~/stores/toast' import { Badge } from '~/ui/lib/Badge' +import type { ComboboxItem } from '~/ui/lib/Combobox' import { Message } from '~/ui/lib/Message' import { ALL_ISH } from '~/util/consts' import { pb } from '~/util/path-builder' -const toComboboxItem = (p: SiloIpPool) => { +const toComboboxItem = (p: SiloIpPool): ComboboxItem => { const value = p.name const selectedLabel = p.name const label = ( From f4146d1bacdfeca840c6e6acf68670d9d4f6aa6f Mon Sep 17 00:00:00 2001 From: Charlie Park Date: Tue, 22 Oct 2024 16:24:21 -0700 Subject: [PATCH 03/19] add vertical space to ImageSelectField combobox item to match --- app/components/form/fields/ImageSelectField.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/components/form/fields/ImageSelectField.tsx b/app/components/form/fields/ImageSelectField.tsx index 2254c0cd00..0f2307c063 100644 --- a/app/components/form/fields/ImageSelectField.tsx +++ b/app/components/form/fields/ImageSelectField.tsx @@ -80,10 +80,10 @@ export function toImageComboboxItem( value: id, selectedLabel: name, label: ( - <> +
{name}
{itemMetadata}
- +
), } } From e0346d8fe69803a8ce899ccf41f6d6f97d7e9b70 Mon Sep 17 00:00:00 2001 From: Charlie Park Date: Tue, 22 Oct 2024 16:46:07 -0700 Subject: [PATCH 04/19] Update Playwright locator --- test/e2e/floating-ip-create.e2e.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/e2e/floating-ip-create.e2e.ts b/test/e2e/floating-ip-create.e2e.ts index dfe5af2091..d1c1198214 100644 --- a/test/e2e/floating-ip-create.e2e.ts +++ b/test/e2e/floating-ip-create.e2e.ts @@ -28,19 +28,19 @@ test('can create a floating IP', async ({ page }) => { .getByRole('textbox', { name: 'Description' }) .fill('A description for this Floating IP') - const poolListbox = page.getByRole('button', { name: 'IP pool' }) + const ipPoolCombobox = page.getByLabel('IP pool') // accordion content should be hidden - await expect(poolListbox).toBeHidden() + await expect(ipPoolCombobox).toBeHidden() // open accordion await page.getByRole('button', { name: 'Advanced' }).click() // accordion content should be visible - await expect(poolListbox).toBeVisible() + await expect(ipPoolCombobox).toBeVisible() // choose pool and submit - await poolListbox.click() + await ipPoolCombobox.click() await page.getByRole('option', { name: 'ip-pool-1' }).click() await page.getByRole('button', { name: 'Create floating IP' }).click() From 9ab6871f4bf3b718a6798cfdb0fbbfe39dbe0ffd Mon Sep 17 00:00:00 2001 From: Charlie Park Date: Thu, 24 Oct 2024 08:36:35 -0700 Subject: [PATCH 05/19] Refactor --- app/forms/floating-ip-create.tsx | 9 +++------ app/forms/ip-pool-create.tsx | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/app/forms/floating-ip-create.tsx b/app/forms/floating-ip-create.tsx index 9f410f8d1e..b8a4f57d54 100644 --- a/app/forms/floating-ip-create.tsx +++ b/app/forms/floating-ip-create.tsx @@ -39,12 +39,9 @@ const toComboboxItem = (p: SiloIpPool): ComboboxItem => {
{p.name} {p.isDefault && ( - <> - {' '} - - default - - + + default + )}
{p.description.length && ( diff --git a/app/forms/ip-pool-create.tsx b/app/forms/ip-pool-create.tsx index 64d864bfbe..f9472b9042 100644 --- a/app/forms/ip-pool-create.tsx +++ b/app/forms/ip-pool-create.tsx @@ -54,7 +54,7 @@ export function CreateIpPoolSideModalForm() { ) From b00cbe91b10124bb4e238c7bee0faa8e84656945 Mon Sep 17 00:00:00 2001 From: Charlie Park Date: Thu, 24 Oct 2024 14:02:52 -0700 Subject: [PATCH 06/19] Update Message copy in edit form --- app/forms/ip-pool-edit.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/forms/ip-pool-edit.tsx b/app/forms/ip-pool-edit.tsx index 71cf051b10..fbfb8508b1 100644 --- a/app/forms/ip-pool-edit.tsx +++ b/app/forms/ip-pool-edit.tsx @@ -71,7 +71,7 @@ export function EditIpPoolSideModalForm() { ) From 5b32e8aa2e19b4bb146d1c7b78c15d6cc43a7ec7 Mon Sep 17 00:00:00 2001 From: Charlie Park Date: Thu, 24 Oct 2024 14:07:40 -0700 Subject: [PATCH 07/19] Consolidate IpPoolVisibilityMessage --- app/forms/ip-pool-create.tsx | 12 ++++++++---- app/forms/ip-pool-edit.tsx | 8 +++----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/app/forms/ip-pool-create.tsx b/app/forms/ip-pool-create.tsx index f9472b9042..e1f87b15d9 100644 --- a/app/forms/ip-pool-create.tsx +++ b/app/forms/ip-pool-create.tsx @@ -52,10 +52,14 @@ export function CreateIpPoolSideModalForm() { > - + ) } + +export const IpPoolVisibilityMessage = () => ( + +) diff --git a/app/forms/ip-pool-edit.tsx b/app/forms/ip-pool-edit.tsx index fbfb8508b1..2b1a15978e 100644 --- a/app/forms/ip-pool-edit.tsx +++ b/app/forms/ip-pool-edit.tsx @@ -20,9 +20,10 @@ import { NameField } from '~/components/form/fields/NameField' import { SideModalForm } from '~/components/form/SideModalForm' import { getIpPoolSelector, useIpPoolSelector } from '~/hooks/use-params' import { addToast } from '~/stores/toast' -import { Message } from '~/ui/lib/Message' import { pb } from '~/util/path-builder' +import { IpPoolVisibilityMessage } from './ip-pool-create' + EditIpPoolSideModalForm.loader = async ({ params }: LoaderFunctionArgs) => { const { pool } = getIpPoolSelector(params) await apiQueryClient.prefetchQuery('ipPoolView', { path: { pool } }) @@ -69,10 +70,7 @@ export function EditIpPoolSideModalForm() { > - + ) } From 712f4a1a1711b1bc74b9743b292cc6c2e6542b90 Mon Sep 17 00:00:00 2001 From: Charlie Park Date: Thu, 24 Oct 2024 16:21:41 -0700 Subject: [PATCH 08/19] Update instance-creats pt.1 --- app/forms/instance-create.tsx | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/app/forms/instance-create.tsx b/app/forms/instance-create.tsx index 35f91958c5..005a662620 100644 --- a/app/forms/instance-create.tsx +++ b/app/forms/instance-create.tsx @@ -25,6 +25,7 @@ import { type InstanceCreate, type InstanceDiskAttachment, type NameOrId, + type SiloIpPool, } from '@oxide/api' import { Images16Icon, @@ -609,7 +610,7 @@ const AdvancedAccordion = ({ }: { control: Control isSubmitting: boolean - siloPools: Array<{ name: string; isDefault: boolean }> + siloPools: Array }) => { // we track this state manually for the sole reason that we need to be able to // tell, inside AccordionItem, when an accordion is opened so we can scroll its @@ -736,11 +737,23 @@ const AdvancedAccordion = ({ items={ siloPools.map((pool) => ({ label: ( -
- {pool.name} - {pool.isDefault && default} +
+
+ {pool.name} + {pool.isDefault && ( + + default + + )} +
+ {pool.description.length && ( +
+ {pool.description} +
+ )}
), + selectedLabel: pool.name, value: pool.name, })) || [] } From 2e76f47e9cb0c297a1714d7ce077234b3513de18 Mon Sep 17 00:00:00 2001 From: Charlie Park Date: Thu, 24 Oct 2024 17:01:37 -0700 Subject: [PATCH 09/19] Update instance-creats pt.2 --- app/forms/instance-create.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/forms/instance-create.tsx b/app/forms/instance-create.tsx index 005a662620..2311ddaa89 100644 --- a/app/forms/instance-create.tsx +++ b/app/forms/instance-create.tsx @@ -61,7 +61,7 @@ import { addToast } from '~/stores/toast' import { Badge } from '~/ui/lib/Badge' import { Button } from '~/ui/lib/Button' import { Checkbox } from '~/ui/lib/Checkbox' -import { toComboboxItems } from '~/ui/lib/Combobox' +import { Combobox, toComboboxItems } from '~/ui/lib/Combobox' import { FormDivider } from '~/ui/lib/Divider' import { EmptyMessage } from '~/ui/lib/EmptyMessage' import { Listbox } from '~/ui/lib/Listbox' @@ -729,11 +729,11 @@ const AdvancedAccordion = ({
{assignEphemeralIp && ( - pool.name === selectedPool)?.name}`} + selectedItemLabel={selectedPool || ''} + selectedItemValue={selectedPool || ''} items={ siloPools.map((pool) => ({ label: ( From 0d80e0480ee82bd2d504be7c017216e6db9c00bc Mon Sep 17 00:00:00 2001 From: Charlie Park Date: Thu, 24 Oct 2024 17:41:38 -0700 Subject: [PATCH 10/19] Update AttachEphemeralIpModal --- app/components/AttachEphemeralIpModal.tsx | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/app/components/AttachEphemeralIpModal.tsx b/app/components/AttachEphemeralIpModal.tsx index 878021a11f..bfc0a3657f 100644 --- a/app/components/AttachEphemeralIpModal.tsx +++ b/app/components/AttachEphemeralIpModal.tsx @@ -10,7 +10,7 @@ import { useMemo } from 'react' import { useForm } from 'react-hook-form' import { useApiMutation, useApiQueryClient, usePrefetchedApiQuery } from '~/api' -import { ListboxField } from '~/components/form/fields/ListboxField' +import { ComboboxField } from '~/components/form/fields/ComboboxField' import { useInstanceSelector } from '~/hooks/use-params' import { addToast } from '~/stores/toast' import { Badge } from '~/ui/lib/Badge' @@ -45,7 +45,7 @@ export const AttachEphemeralIpModal = ({ onDismiss }: { onDismiss: () => void })
- void }) items={ siloPools?.items.map((pool) => ({ label: ( -
- {pool.name} - {pool.isDefault && default} +
+
+ {pool.name} + {pool.isDefault && ( + + default + + )} +
+ {pool.description.length && ( +
+ {pool.description} +
+ )}
), + selectedLabel: pool.name, value: pool.name, })) || [] } From d18c0675dd140092c282224c882b387406fc0be4 Mon Sep 17 00:00:00 2001 From: Charlie Park Date: Fri, 25 Oct 2024 10:41:28 -0700 Subject: [PATCH 11/19] Update test --- test/e2e/instance-create.e2e.ts | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/test/e2e/instance-create.e2e.ts b/test/e2e/instance-create.e2e.ts index 628b92b9b5..e048b0e7d1 100644 --- a/test/e2e/instance-create.e2e.ts +++ b/test/e2e/instance-create.e2e.ts @@ -70,27 +70,24 @@ test('can create an instance', async ({ page }) => { await page.getByRole('button', { name: 'Networking' }).click() await page.getByRole('button', { name: 'Configuration' }).click() - const assignEphemeralIpCheckbox = page.getByRole('checkbox', { + const checkbox = page.getByRole('checkbox', { name: 'Allocate and attach an ephemeral IP address', }) - const assignEphemeralIpButton = page.getByRole('button', { - name: 'IP pool for ephemeral IP', - }) + const label = page.getByLabel('IP pool for ephemeral IP') // verify that the ip pool selector is visible and default is selected - await expect(assignEphemeralIpCheckbox).toBeChecked() - await assignEphemeralIpButton.click() + await expect(checkbox).toBeChecked() + await label.click() await expect(page.getByRole('option', { name: 'ip-pool-1' })).toBeEnabled() - await assignEphemeralIpButton.click() // click closes the listbox so we can do more stuff // unchecking the box should disable the selector - await assignEphemeralIpCheckbox.uncheck() - await expect(assignEphemeralIpButton).toBeHidden() + await checkbox.uncheck() + await expect(label).toBeHidden() // re-checking the box should re-enable the selector, and other options should be selectable - await assignEphemeralIpCheckbox.check() - await assignEphemeralIpButton.click() - await page.getByRole('option', { name: 'ip-pool-2' }).click() + await checkbox.check() + await page.getByRole('combobox', { name: 'IP pool for ephemeral IP' }).clear() + await page.getByRole('option', { name: 'ip-pool-2 VPN IPs' }).click() // should be visible in accordion await expect(page.getByRole('radiogroup', { name: 'Network interface' })).toBeVisible() From 5f1b9fb7b023dd2319db66500467beb8962677fe Mon Sep 17 00:00:00 2001 From: Charlie Park Date: Fri, 25 Oct 2024 11:21:21 -0700 Subject: [PATCH 12/19] Back to Listbox for modal --- app/components/AttachEphemeralIpModal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/components/AttachEphemeralIpModal.tsx b/app/components/AttachEphemeralIpModal.tsx index bfc0a3657f..3733b375ae 100644 --- a/app/components/AttachEphemeralIpModal.tsx +++ b/app/components/AttachEphemeralIpModal.tsx @@ -10,7 +10,7 @@ import { useMemo } from 'react' import { useForm } from 'react-hook-form' import { useApiMutation, useApiQueryClient, usePrefetchedApiQuery } from '~/api' -import { ComboboxField } from '~/components/form/fields/ComboboxField' +import { ListboxField } from '~/components/form/fields/ListboxField' import { useInstanceSelector } from '~/hooks/use-params' import { addToast } from '~/stores/toast' import { Badge } from '~/ui/lib/Badge' @@ -45,7 +45,7 @@ export const AttachEphemeralIpModal = ({ onDismiss }: { onDismiss: () => void }) - Date: Fri, 25 Oct 2024 13:08:43 -0700 Subject: [PATCH 13/19] Revert to Listboxes --- app/forms/floating-ip-create.tsx | 9 ++++----- app/forms/instance-create.tsx | 8 ++++---- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/app/forms/floating-ip-create.tsx b/app/forms/floating-ip-create.tsx index b8a4f57d54..70c336142c 100644 --- a/app/forms/floating-ip-create.tsx +++ b/app/forms/floating-ip-create.tsx @@ -19,19 +19,18 @@ import { } from '@oxide/api' import { AccordionItem } from '~/components/AccordionItem' -import { ComboboxField } from '~/components/form/fields/ComboboxField' import { DescriptionField } from '~/components/form/fields/DescriptionField' +import { ListboxField } from '~/components/form/fields/ListboxField' import { NameField } from '~/components/form/fields/NameField' import { SideModalForm } from '~/components/form/SideModalForm' import { useProjectSelector } from '~/hooks/use-params' import { addToast } from '~/stores/toast' import { Badge } from '~/ui/lib/Badge' -import type { ComboboxItem } from '~/ui/lib/Combobox' import { Message } from '~/ui/lib/Message' import { ALL_ISH } from '~/util/consts' import { pb } from '~/util/path-builder' -const toComboboxItem = (p: SiloIpPool): ComboboxItem => { +const toListboxItem = (p: SiloIpPool) => { const value = p.name const selectedLabel = p.name const label = ( @@ -109,9 +108,9 @@ export function CreateFloatingIpSideModalForm() { content="If you don’t specify a pool, the default will be used" /> - toComboboxItem(p))} + items={(allPools?.items || []).map((p) => toListboxItem(p))} label="IP pool" control={form.control} placeholder="Select a pool" diff --git a/app/forms/instance-create.tsx b/app/forms/instance-create.tsx index 2311ddaa89..005a662620 100644 --- a/app/forms/instance-create.tsx +++ b/app/forms/instance-create.tsx @@ -61,7 +61,7 @@ import { addToast } from '~/stores/toast' import { Badge } from '~/ui/lib/Badge' import { Button } from '~/ui/lib/Button' import { Checkbox } from '~/ui/lib/Checkbox' -import { Combobox, toComboboxItems } from '~/ui/lib/Combobox' +import { toComboboxItems } from '~/ui/lib/Combobox' import { FormDivider } from '~/ui/lib/Divider' import { EmptyMessage } from '~/ui/lib/EmptyMessage' import { Listbox } from '~/ui/lib/Listbox' @@ -729,11 +729,11 @@ const AdvancedAccordion = ({
{assignEphemeralIp && ( - pool.name === selectedPool)?.name}`} items={ siloPools.map((pool) => ({ label: ( From 820f39c7aaec14df47917481bb81050816ca017a Mon Sep 17 00:00:00 2001 From: Charlie Park Date: Fri, 25 Oct 2024 13:32:48 -0700 Subject: [PATCH 14/19] Update tests --- test/e2e/floating-ip-create.e2e.ts | 8 ++++---- test/e2e/instance-create.e2e.ts | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/test/e2e/floating-ip-create.e2e.ts b/test/e2e/floating-ip-create.e2e.ts index d1c1198214..4bedc596ce 100644 --- a/test/e2e/floating-ip-create.e2e.ts +++ b/test/e2e/floating-ip-create.e2e.ts @@ -28,19 +28,19 @@ test('can create a floating IP', async ({ page }) => { .getByRole('textbox', { name: 'Description' }) .fill('A description for this Floating IP') - const ipPoolCombobox = page.getByLabel('IP pool') + const label = page.getByLabel('IP pool') // accordion content should be hidden - await expect(ipPoolCombobox).toBeHidden() + await expect(label).toBeHidden() // open accordion await page.getByRole('button', { name: 'Advanced' }).click() // accordion content should be visible - await expect(ipPoolCombobox).toBeVisible() + await expect(label).toBeVisible() // choose pool and submit - await ipPoolCombobox.click() + await label.click() await page.getByRole('option', { name: 'ip-pool-1' }).click() await page.getByRole('button', { name: 'Create floating IP' }).click() diff --git a/test/e2e/instance-create.e2e.ts b/test/e2e/instance-create.e2e.ts index e048b0e7d1..e2e2125100 100644 --- a/test/e2e/instance-create.e2e.ts +++ b/test/e2e/instance-create.e2e.ts @@ -86,8 +86,7 @@ test('can create an instance', async ({ page }) => { // re-checking the box should re-enable the selector, and other options should be selectable await checkbox.check() - await page.getByRole('combobox', { name: 'IP pool for ephemeral IP' }).clear() - await page.getByRole('option', { name: 'ip-pool-2 VPN IPs' }).click() + await selectOption(page, 'IP pool for ephemeral IP', 'ip-pool-2 VPN IPs') // should be visible in accordion await expect(page.getByRole('radiogroup', { name: 'Network interface' })).toBeVisible() From 0f63b9bd7526b999cf563f36bbecad92ab2f0947 Mon Sep 17 00:00:00 2001 From: David Crespo Date: Mon, 28 Oct 2024 16:33:04 -0500 Subject: [PATCH 15/19] share toIpPoolItem --- app/components/AttachEphemeralIpModal.tsx | 27 +++---------------- app/components/form/fields/ip-pool-item.tsx | 30 +++++++++++++++++++++ app/forms/floating-ip-create.tsx | 26 ++---------------- app/forms/instance-create.tsx | 26 ++---------------- 4 files changed, 37 insertions(+), 72 deletions(-) create mode 100644 app/components/form/fields/ip-pool-item.tsx diff --git a/app/components/AttachEphemeralIpModal.tsx b/app/components/AttachEphemeralIpModal.tsx index 3733b375ae..f50c83589d 100644 --- a/app/components/AttachEphemeralIpModal.tsx +++ b/app/components/AttachEphemeralIpModal.tsx @@ -13,10 +13,11 @@ import { useApiMutation, useApiQueryClient, usePrefetchedApiQuery } from '~/api' import { ListboxField } from '~/components/form/fields/ListboxField' import { useInstanceSelector } from '~/hooks/use-params' import { addToast } from '~/stores/toast' -import { Badge } from '~/ui/lib/Badge' import { Modal } from '~/ui/lib/Modal' import { ALL_ISH } from '~/util/consts' +import { toIpPoolItem } from './form/fields/ip-pool-item' + export const AttachEphemeralIpModal = ({ onDismiss }: { onDismiss: () => void }) => { const queryClient = useApiQueryClient() const { project, instance } = useInstanceSelector() @@ -54,29 +55,7 @@ export const AttachEphemeralIpModal = ({ onDismiss }: { onDismiss: () => void }) ? 'Select a pool' : 'No pools available' } - items={ - siloPools?.items.map((pool) => ({ - label: ( -
-
- {pool.name} - {pool.isDefault && ( - - default - - )} -
- {pool.description.length && ( -
- {pool.description} -
- )} -
- ), - selectedLabel: pool.name, - value: pool.name, - })) || [] - } + items={siloPools.items.map(toIpPoolItem)} required /> diff --git a/app/components/form/fields/ip-pool-item.tsx b/app/components/form/fields/ip-pool-item.tsx new file mode 100644 index 0000000000..0f002a8e17 --- /dev/null +++ b/app/components/form/fields/ip-pool-item.tsx @@ -0,0 +1,30 @@ +/* + * 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 type { SiloIpPool } from '~/api' +import { Badge } from '~/ui/lib/Badge' + +export function toIpPoolItem(p: SiloIpPool) { + const value = p.name + const selectedLabel = p.name + const label = ( +
+
+ {p.name} + {p.isDefault && ( + + default + + )} +
+ {p.description.length && ( +
{p.description}
+ )} +
+ ) + return { value, selectedLabel, label } +} diff --git a/app/forms/floating-ip-create.tsx b/app/forms/floating-ip-create.tsx index 70c336142c..65a9d742bf 100644 --- a/app/forms/floating-ip-create.tsx +++ b/app/forms/floating-ip-create.tsx @@ -15,42 +15,20 @@ import { useApiQuery, useApiQueryClient, type FloatingIpCreate, - type SiloIpPool, } from '@oxide/api' import { AccordionItem } from '~/components/AccordionItem' import { DescriptionField } from '~/components/form/fields/DescriptionField' +import { toIpPoolItem } from '~/components/form/fields/ip-pool-item' import { ListboxField } from '~/components/form/fields/ListboxField' import { NameField } from '~/components/form/fields/NameField' import { SideModalForm } from '~/components/form/SideModalForm' import { useProjectSelector } from '~/hooks/use-params' import { addToast } from '~/stores/toast' -import { Badge } from '~/ui/lib/Badge' import { Message } from '~/ui/lib/Message' import { ALL_ISH } from '~/util/consts' import { pb } from '~/util/path-builder' -const toListboxItem = (p: SiloIpPool) => { - const value = p.name - const selectedLabel = p.name - const label = ( -
-
- {p.name} - {p.isDefault && ( - - default - - )} -
- {p.description.length && ( -
{p.description}
- )} -
- ) - return { value, selectedLabel, label } -} - const defaultValues: Omit = { name: '', description: '', @@ -110,7 +88,7 @@ export function CreateFloatingIpSideModalForm() { toListboxItem(p))} + items={(allPools?.items || []).map(toIpPoolItem)} label="IP pool" control={form.control} placeholder="Select a pool" diff --git a/app/forms/instance-create.tsx b/app/forms/instance-create.tsx index 005a662620..be7e9eaa2d 100644 --- a/app/forms/instance-create.tsx +++ b/app/forms/instance-create.tsx @@ -47,6 +47,7 @@ import { } from '~/components/form/fields/DisksTableField' import { FileField } from '~/components/form/fields/FileField' import { BootDiskImageSelectField as ImageSelectField } from '~/components/form/fields/ImageSelectField' +import { toIpPoolItem } from '~/components/form/fields/ip-pool-item' import { NameField } from '~/components/form/fields/NameField' import { NetworkInterfaceField } from '~/components/form/fields/NetworkInterfaceField' import { NumberField } from '~/components/form/fields/NumberField' @@ -58,7 +59,6 @@ import { FullPageForm } from '~/components/form/FullPageForm' import { HL } from '~/components/HL' import { getProjectSelector, useProjectSelector } from '~/hooks/use-params' import { addToast } from '~/stores/toast' -import { Badge } from '~/ui/lib/Badge' import { Button } from '~/ui/lib/Button' import { Checkbox } from '~/ui/lib/Checkbox' import { toComboboxItems } from '~/ui/lib/Combobox' @@ -734,29 +734,7 @@ const AdvancedAccordion = ({ label="IP pool for ephemeral IP" placeholder={defaultPool ? `${defaultPool} (default)` : 'Select a pool'} selected={`${siloPools.find((pool) => pool.name === selectedPool)?.name}`} - items={ - siloPools.map((pool) => ({ - label: ( -
-
- {pool.name} - {pool.isDefault && ( - - default - - )} -
- {pool.description.length && ( -
- {pool.description} -
- )} -
- ), - selectedLabel: pool.name, - value: pool.name, - })) || [] - } + items={siloPools.map(toIpPoolItem)} disabled={!assignEphemeralIp || isSubmitting} required onChange={(value) => { From ffccd25c2178d235e312a948827a83191f140716 Mon Sep 17 00:00:00 2001 From: Charlie Park Date: Mon, 28 Oct 2024 18:53:16 -0700 Subject: [PATCH 16/19] update badge CSS on menu-item dropdown --- app/ui/styles/components/menu-list.css | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/ui/styles/components/menu-list.css b/app/ui/styles/components/menu-list.css index 4241dec636..6cbb03ee5f 100644 --- a/app/ui/styles/components/menu-list.css +++ b/app/ui/styles/components/menu-list.css @@ -12,6 +12,10 @@ .ox-menu-item { @apply relative cursor-pointer px-3 py-2 text-sans-md text-default; + .ox-badge { + @apply bg-transparent; + } + } .ox-menu-item.is-highlighted { @@ -28,6 +32,10 @@ .ox-menu-item.is-selected { @apply border-0 text-accent bg-accent-secondary hover:bg-accent-secondary-hover; + + .ox-badge { + @apply text-accent ring-[rgba(var(--base-green-800-rgb),0.15)] + } } /* beautiful ring */ From 0f50033b6ab35d63da7d8563618d69169a5d3e4e Mon Sep 17 00:00:00 2001 From: Charlie Park Date: Mon, 28 Oct 2024 18:54:18 -0700 Subject: [PATCH 17/19] formatting --- app/ui/styles/components/menu-list.css | 1 - 1 file changed, 1 deletion(-) diff --git a/app/ui/styles/components/menu-list.css b/app/ui/styles/components/menu-list.css index 6cbb03ee5f..1946a289d8 100644 --- a/app/ui/styles/components/menu-list.css +++ b/app/ui/styles/components/menu-list.css @@ -32,7 +32,6 @@ .ox-menu-item.is-selected { @apply border-0 text-accent bg-accent-secondary hover:bg-accent-secondary-hover; - .ox-badge { @apply text-accent ring-[rgba(var(--base-green-800-rgb),0.15)] } From 3ca75373fccc194e246b41254f97d060fd04d9d4 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2024 01:55:27 +0000 Subject: [PATCH 18/19] Bot commit: format with prettier --- app/ui/styles/components/menu-list.css | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/ui/styles/components/menu-list.css b/app/ui/styles/components/menu-list.css index 1946a289d8..576f3ca2f5 100644 --- a/app/ui/styles/components/menu-list.css +++ b/app/ui/styles/components/menu-list.css @@ -15,7 +15,6 @@ .ox-badge { @apply bg-transparent; } - } .ox-menu-item.is-highlighted { @@ -33,7 +32,7 @@ .ox-menu-item.is-selected { @apply border-0 text-accent bg-accent-secondary hover:bg-accent-secondary-hover; .ox-badge { - @apply text-accent ring-[rgba(var(--base-green-800-rgb),0.15)] + @apply ring-[rgba(var(--base-green-800-rgb),0.15)] text-accent; } } From 1a30d3e3f82a06ea9329c56b1e0fea5467067811 Mon Sep 17 00:00:00 2001 From: Charlie Park Date: Tue, 29 Oct 2024 10:55:09 -0700 Subject: [PATCH 19/19] update menu item badge style per design feedback --- app/components/form/fields/ip-pool-item.tsx | 2 +- app/ui/styles/components/menu-list.css | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/app/components/form/fields/ip-pool-item.tsx b/app/components/form/fields/ip-pool-item.tsx index 0f002a8e17..eafed84c71 100644 --- a/app/components/form/fields/ip-pool-item.tsx +++ b/app/components/form/fields/ip-pool-item.tsx @@ -16,7 +16,7 @@ export function toIpPoolItem(p: SiloIpPool) {
{p.name} {p.isDefault && ( - + default )} diff --git a/app/ui/styles/components/menu-list.css b/app/ui/styles/components/menu-list.css index 576f3ca2f5..3aa0d0ff10 100644 --- a/app/ui/styles/components/menu-list.css +++ b/app/ui/styles/components/menu-list.css @@ -12,9 +12,6 @@ .ox-menu-item { @apply relative cursor-pointer px-3 py-2 text-sans-md text-default; - .ox-badge { - @apply bg-transparent; - } } .ox-menu-item.is-highlighted { @@ -32,7 +29,7 @@ .ox-menu-item.is-selected { @apply border-0 text-accent bg-accent-secondary hover:bg-accent-secondary-hover; .ox-badge { - @apply ring-[rgba(var(--base-green-800-rgb),0.15)] text-accent; + @apply ring-0 text-inverse bg-accent; } }