Implementation:TobikoData Sqlmesh SelectEnvironment
| Knowledge Sources | |
|---|---|
| Domains | Web_UI, Environment_Management |
| Last Updated | 2026-02-07 20:00 GMT |
Overview
A dropdown menu component for selecting and managing SQLMesh environments with create, delete, and switch functionality.
Description
SelectEnvironment is a sophisticated React component that provides a dropdown interface for managing SQLMesh environments. It displays all available environments (both local and remote) with visual indicators for environment type, status, and features (production, default, pinned). The component supports creating new environments, deleting remote environments, removing local environments, and switching between environments with appropriate callbacks and confirmations.
The dropdown uses HeadlessUI's Menu component for accessible menu interactions and displays rich information about each environment including its type (local/remote), whether it's a production environment, default environment, or pinned environment. The component implements smart button styling based on environment state, with different colors for local vs remote environments and disabled states. It also includes an AddEnvironment component at the bottom of the dropdown for creating new environments.
Usage
Use this component whenever users need to view, select, or manage SQLMesh environments. It's typically placed in the application header or toolbar for easy access across the application.
Code Reference
Source Location
- Repository: TobikoData_Sqlmesh
- File: web/client/src/library/components/environmentDetails/SelectEnvironment.tsx
Signature
export function SelectEnvironment({
disabled = false,
className,
showAddEnvironment = true,
size = EnumSize.sm,
onSelect,
}: {
className?: string
size?: ButtonSize
disabled?: boolean
showAddEnvironment?: boolean
onSelect?: () => void
}): JSX.Element
Import
import { SelectEnvironment } from '@components/environmentDetails/SelectEnvironment'
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| disabled | boolean | No | Whether the selector is disabled (default: false) |
| className | string | No | Additional CSS classes for styling |
| showAddEnvironment | boolean | No | Show the add environment form at bottom (default: true) |
| size | ButtonSize | No | Size of the button (default: EnumSize.sm) |
| onSelect | () => void | No | Callback when an environment is selected |
Outputs
| Name | Type | Description |
|---|---|---|
| JSX.Element | JSX.Element | The rendered dropdown menu component |
Component Structure
Menu Button
The button displays the current environment with type indicator:
<ButtonMenu
variant={EnumVariant.Info}
size={size}
disabled={disabled}
className="flex justify-between w-full mx-0 pr-1"
>
<span className={clsx(
'block overflow-hidden truncate',
(environment.isLocal || disabled) && 'text-neutral-500',
environment.isRemote
? 'text-primary-600 dark:text-primary-300'
: 'text-neutral-600 dark:text-neutral-200',
)}>
<span className="text-xs text-neutral-300 dark:text-neutral-500 mr-1">
Environment:
</span>
<span>{environment.name}</span>
</span>
<ChevronDownIcon className="w-4" />
</ButtonMenu>
Environment Colors
| State | Color | Description |
|---|---|---|
| Remote | Primary (blue) | Connected to backend |
| Local | Neutral (gray) | Local-only environment |
| Disabled | Neutral (muted) | Interaction disabled |
Environment List Items
Each environment displays comprehensive information:
<Menu.Item
key={env.name}
disabled={env === environment}
>
{({ active }) => (
<div onClick={e => handleSelectEnvironment(e, env)}>
<CheckCircleIcon /> {/* Active indicator */}
<span>
{env.name}
<small>({env.type})</small>
</span>
{env.isProd && <small>Production Environment</small>}
{env.isDefault && <small>Default Environment</small>}
{env.isPinned && <StarIcon />}
{/* Delete/Remove buttons for non-pinned environments */}
</div>
)}
</Menu.Item>
Environment Indicators
| Indicator | Icon | Condition |
|---|---|---|
| Active | CheckCircleIcon (primary) | Current environment selected |
| Pinned | StarIcon (primary) | Environment is pinned |
| Production | Text label | Environment is production |
| Default | Text label | Environment is default target |
Environment Actions
Selecting an Environment
function handleSelectEnvironment(e: MouseEvent, env: ModelEnvironment): void {
e.stopPropagation()
setEnvironment(env)
onSelect?.()
}
Removing Local Environments
function handleRemoveLocalEnvironment(
e: MouseEvent,
env: ModelEnvironment,
): void {
e.stopPropagation()
removeLocalEnvironment(env)
}
Shows minus icon button for local, non-pinned environments.
Deleting Remote Environments
function handleDeleteEnvironment(e: MouseEvent, env: ModelEnvironment): void {
e.stopPropagation()
apiDeleteEnvironment(env.name)
.then(() => {
removeLocalEnvironment(env)
})
.catch(error => {
addError(EnumErrorKey.Environments, error)
})
}
Shows "Delete" button for remote, non-pinned environments.
Action Button Visibility Rules
| Environment Type | Pinned | Is Current | Action Button |
|---|---|---|---|
| Local | No | No | Remove (minus icon) |
| Local | Yes | - | None |
| Local | - | Yes | None |
| Remote | No | No | Delete (button) |
| Remote | Yes | - | None |
| Remote | - | Yes | None |
Rule: Only non-pinned, non-current environments show action buttons.
Add Environment Section
At the bottom of the dropdown:
{showAddEnvironment && (
<>
<Divider />
<AddEnvironment
onAdd={close}
className="p-2 pb-2"
/>
</>
)}
The onAdd callback receives the close function from HeadlessUI, allowing the menu to automatically close after adding an environment.
Menu Styling
The dropdown menu uses absolute positioning:
<div className="!mx-0 absolute top-10 right-0 min-w-[16rem] max-w-[100%] overflow-hidden shadow-2xl bg-theme border-0 border-neutral-100 dark:border-neutral-800 rounded-md flex flex-col z-50">
<Menu.Items className="mx-0 overflow-auto max-h-80 hover:scrollbar scrollbar--vertical">
{/* Environment items */}
</Menu.Items>
</div>
Menu Features
- Position: Absolute, 10px below button, right-aligned
- Size: Min 16rem width, max 100% width, max 80vh height
- Scrolling: Vertical scroll for long environment lists
- Styling: Themed background, shadow, rounded corners
- Z-index: 50 (appears above most content)
Disabled State
When disabled:
disabled={
isFetchingPlanRun ||
planAction.isProcessing ||
environment.isInitialProd
}
- Button shows muted colors
- Dropdown cannot be opened
- Chevron icon is grayed out
Common reasons:
- Plan is running
- Fetching plan data
- Initial production environment (cannot switch)
Menu Item States
HeadlessUI provides automatic state management:
<Menu.Item disabled={env === environment}>
{({ active }) => (
<div className={clsx(
active
? 'text-neutral-600 bg-neutral-10'
: 'text-neutral-400',
env === environment
? 'cursor-default bg-neutral-5'
: 'cursor-pointer',
)}>
{/* Content */}
</div>
)}
</Menu.Item>
States
| State | Background | Cursor | Condition |
|---|---|---|---|
| Active (hover) | neutral-10 | pointer | Mouse over item |
| Selected | neutral-5 | default | Current environment |
| Inactive | transparent | pointer | Other environments |
Transitions
The menu uses HeadlessUI's Transition:
<Transition
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
{/* Menu.Items */}
</Transition>
- Enter: Instant (no transition)
- Leave: 100ms fade out
Usage Example
import { SelectEnvironment } from '@components/environmentDetails/SelectEnvironment'
// Basic usage
<SelectEnvironment />
// With custom configuration
<SelectEnvironment
className="w-64"
size={EnumSize.md}
disabled={isProcessing}
showAddEnvironment={false}
onSelect={() => console.log('Environment changed')}
/>