refactor(workspaces): remove replaced host combobox (#13507)

This commit is contained in:
Neil
2026-08-10 20:25:13 -07:00
committed by GitHub
parent 837ea3feec
commit 9359edc2b0
8 changed files with 0 additions and 288 deletions
@@ -1502,12 +1502,6 @@
"auto.components.new.workspace.ProjectCombobox.search": {
"ko": "프로젝트 검색..."
},
"auto.components.new.workspace.ProjectHostSetupCombobox.empty": {
"ko": "이 프로젝트에 준비된 호스트가 없습니다."
},
"auto.components.new.workspace.ProjectHostSetupCombobox.placeholder": {
"ko": "호스트 선택"
},
"auto.components.notification.sound.options.0acd3d384e": {
"ko": "클랙"
},
@@ -1,157 +0,0 @@
// @vitest-environment happy-dom
import React, { act } from 'react'
import { createRoot, type Root } from 'react-dom/client'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type {
NeedsSetupProjectHostOption,
ProjectHostSetupOption
} from '@/lib/project-host-setup-options'
import ProjectHostSetupCombobox from './ProjectHostSetupCombobox'
vi.mock('@/components/ui/popover', () => ({
Popover: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
PopoverContent: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
PopoverTrigger: ({ children }: { children: React.ReactNode }) => <>{children}</>
}))
vi.mock('@/components/ui/command', () => ({
Command: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
CommandEmpty: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
CommandList: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
CommandItem: ({
children,
disabled,
onSelect,
value
}: {
children: React.ReactNode
disabled?: boolean
onSelect?: (value: string) => void
value: string
}) => (
<button
type="button"
data-command-value={value}
disabled={disabled}
onClick={() => onSelect?.(value)}
>
{children}
</button>
)
}))
let container: HTMLDivElement
let root: Root
const readyOption: ProjectHostSetupOption = {
id: 'local-setup',
kind: 'ready',
projectId: 'project-1',
hostId: 'local',
repoId: 'local-repo',
label: 'Local Mac',
detail: 'Orca',
path: '/Users/alice/orca'
}
const needsSetupOption: NeedsSetupProjectHostOption = {
id: 'needs-setup:ssh:builder',
kind: 'needs-setup',
projectId: 'project-1',
hostId: 'ssh:builder',
label: 'Builder',
detail: 'Project location not set',
isAvailable: true,
attention: false
}
const unavailableOption: NeedsSetupProjectHostOption = {
id: 'needs-setup:runtime:old',
kind: 'needs-setup',
projectId: 'project-1',
hostId: 'runtime:old',
label: 'Old server',
detail: 'Update Orca on this host to set up projects',
isAvailable: false,
attention: false
}
beforeEach(() => {
container = document.createElement('div')
document.body.appendChild(container)
root = createRoot(container)
})
afterEach(() => {
act(() => {
root.unmount()
})
container.remove()
})
function renderCombobox({
onValueChange = vi.fn()
}: {
onValueChange?: (setupId: string) => void
} = {}): void {
act(() => {
root.render(
<ProjectHostSetupCombobox
options={[readyOption, needsSetupOption]}
value={readyOption.id}
onValueChange={onValueChange}
/>
)
})
}
describe('ProjectHostSetupCombobox', () => {
it('routes ready setup rows through onValueChange', () => {
const onValueChange = vi.fn()
renderCombobox({ onValueChange })
act(() => {
container
.querySelector<HTMLButtonElement>('[data-command-value="local-setup"]')
?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
})
expect(onValueChange).toHaveBeenCalledWith('local-setup')
})
it('hides hosts that need setup from the run target list', () => {
const onValueChange = vi.fn()
renderCombobox({ onValueChange })
expect(
container.querySelector<HTMLButtonElement>('[data-command-value="needs-setup:ssh:builder"]')
).toBeNull()
expect(container.textContent).not.toContain('Project location not set')
expect(onValueChange).not.toHaveBeenCalled()
})
it('hides unavailable setup rows from the run target list', () => {
const onValueChange = vi.fn()
act(() => {
root.render(
<ProjectHostSetupCombobox
options={[readyOption, unavailableOption]}
value={readyOption.id}
onValueChange={onValueChange}
/>
)
})
const unavailableButton = container.querySelector<HTMLButtonElement>(
'[data-command-value="needs-setup:runtime:old"]'
)
expect(unavailableButton).toBeNull()
expect(container.textContent).not.toContain('Update Orca on this host')
expect(onValueChange).not.toHaveBeenCalled()
})
})
@@ -1,105 +0,0 @@
import React from 'react'
import { Check, ChevronsUpDown, Server } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Command, CommandEmpty, CommandItem, CommandList } from '@/components/ui/command'
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
import { cn } from '@/lib/utils'
import type { ProjectHostSetupOption } from '@/lib/project-host-setup-options'
import { translate } from '@/i18n/i18n'
type ProjectHostSetupComboboxProps = {
options: readonly ProjectHostSetupOption[]
value: string | null
onValueChange: (setupId: string) => void
}
export default function ProjectHostSetupCombobox({
options,
value,
onValueChange
}: ProjectHostSetupComboboxProps): React.JSX.Element {
const [open, setOpen] = React.useState(false)
const readyOptions = options.filter((option) => option.kind === 'ready')
const selected = readyOptions.find((option) => option.id === value) ?? readyOptions[0] ?? null
const handleSelect = React.useCallback(
(setupId: string): void => {
const option = options.find((candidate) => candidate.id === setupId)
if (!option) {
return
}
if (!readyOptions.some((candidate) => candidate.id === setupId)) {
return
}
onValueChange(setupId)
setOpen(false)
},
[onValueChange, options, readyOptions]
)
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
type="button"
variant="outline"
role="combobox"
aria-expanded={open}
className="h-9 w-full justify-between border-input px-3 text-sm font-normal focus:border-ring focus:ring-[3px] focus:ring-ring/50"
>
{selected ? (
<span className="inline-flex min-w-0 items-center gap-1.5">
<Server className="size-3.5 shrink-0 text-muted-foreground" />
<span className="truncate">{selected.label}</span>
</span>
) : (
<span className="text-muted-foreground">
{translate(
'auto.components.new.workspace.ProjectHostSetupCombobox.placeholder',
'Choose host'
)}
</span>
)}
<ChevronsUpDown className="size-3.5 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent
align="start"
className="w-[var(--radix-popover-trigger-width)] min-w-[18rem] p-0"
>
<Command value={selected?.id ?? ''}>
<CommandList>
<CommandEmpty>
{translate(
'auto.components.new.workspace.ProjectHostSetupCombobox.empty',
'No hosts are ready for this project.'
)}
</CommandEmpty>
{readyOptions.map((option) => (
<CommandItem
key={option.id}
value={option.id}
onSelect={() => handleSelect(option.id)}
className="items-center gap-2 px-3 py-2"
>
<Check
className={cn(
'size-4 text-foreground',
option.id === selected?.id ? 'opacity-100' : 'opacity-0'
)}
/>
<Server className="size-3.5 shrink-0 text-muted-foreground" />
<div className="min-w-0 flex-1">
<div className="truncate text-sm">{option.label}</div>
<div className="mt-0.5 truncate text-[11px] text-muted-foreground">
{option.path}
</div>
</div>
</CommandItem>
))}
</CommandList>
</Command>
</PopoverContent>
</Popover>
)
}
-4
View File
@@ -12254,10 +12254,6 @@
"jiraSelectBindFailed": "Couldnt link this Jira issue. Pick the matching site or reconnect Jira, then try again.",
"emoji": "Emoji"
},
"ProjectHostSetupCombobox": {
"empty": "No hosts are ready for this project.",
"placeholder": "Choose host"
},
"ProjectCombobox": {
"empty": "No projects match your search.",
"addProject": "Add a new project",
-4
View File
@@ -11742,10 +11742,6 @@
"jiraSelectBindFailed": "Couldnt link this Jira issue. Pick the matching site or reconnect Jira, then try again.",
"emoji": "Emoji"
},
"ProjectHostSetupCombobox": {
"empty": "No hay hosts listos para este proyecto.",
"placeholder": "Elegir host"
},
"ProjectCombobox": {
"empty": "Ningún proyecto coincide con tu búsqueda.",
"addProject": "Add a new project",
-4
View File
@@ -11742,10 +11742,6 @@
"jiraSelectBindFailed": "この Jira Issue をリンクできませんでした。該当するサイトを選択するか Jira に再接続してから、もう一度お試しください。",
"emoji": "絵文字"
},
"ProjectHostSetupCombobox": {
"empty": "このプロジェクトで準備が整ったホストはありません。",
"placeholder": "ホストを選択"
},
"ProjectCombobox": {
"empty": "検索条件に一致するプロジェクトはありません。",
"addProject": "新規プロジェクトを追加",
-4
View File
@@ -11742,10 +11742,6 @@
"jiraSelectBindFailed": "Couldnt link this Jira issue. Pick the matching site or reconnect Jira, then try again.",
"emoji": "Emoji"
},
"ProjectHostSetupCombobox": {
"empty": "이 프로젝트에 준비된 호스트가 없습니다.",
"placeholder": "호스트 선택"
},
"ProjectCombobox": {
"empty": "검색과 일치하는 프로젝트가 없습니다.",
"addProject": "새 프로젝트 추가",
-4
View File
@@ -11762,10 +11762,6 @@
"jiraSelectBindFailed": "无法关联此 Jira 议题。请选择匹配的站点或重新连接 Jira,然后重试。",
"emoji": "Emoji"
},
"ProjectHostSetupCombobox": {
"empty": "此项目没有已就绪的主机。",
"placeholder": "选择主机"
},
"ProjectCombobox": {
"empty": "没有匹配搜索的项目。",
"addProject": "添加新项目",