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
Original file line number Diff line number Diff line change
Expand Up @@ -288,11 +288,15 @@ describe('ExternalSecretsTab', () => {
)

await userEvent.click(screen.getByRole('button', { name: /add secret/i }))
await userEvent.click(screen.getByRole('menuitem', { name: 'Add secret as variable' }))

const modalContent = openModal.mock.calls[0][0].content as ReactElement<{
onSubmit: (secret: AddSecretModalSubmitData) => Promise<void>
isFile?: boolean
}>

expect(modalContent.props.isFile).toBe(false)

await modalContent.props.onSubmit({
name: 'MY_EXTERNAL_SECRET',
reference: 'prod/database/credentials',
Expand All @@ -315,6 +319,43 @@ describe('ExternalSecretsTab', () => {
expect(onCreateSecret).toHaveBeenCalled()
})

it('should open the file secret creation modal from the empty state dropdown', async () => {
const openModal = jest.fn()
useModalMock.mockReturnValue({
openModal,
closeModal: jest.fn(),
})
useVariablesMock.mockReturnValue({
data: [],
isLoading: false,
} as ReturnType<typeof useVariables>)
useVariablesSecretManagersMock.mockReturnValue({
secretManagers: [
{
id: 'sm-1',
name: 'Prod secret manager',
created_at: '2026-01-01T00:00:00.000Z',
updated_at: '2026-01-01T00:00:00.000Z',
endpoint: { mode: 'AWS_SECRET_MANAGER' },
authentication: { mode: 'STS' },
},
],
hasClusterSecretManagerConfigured: true,
clusterId: 'cluster-id',
})

const { userEvent } = renderWithProviders(<ExternalSecretsTab scope="APPLICATION" parentId="service-id" />)

await userEvent.click(screen.getByRole('button', { name: /add secret/i }))
await userEvent.click(screen.getByRole('menuitem', { name: 'Add secret as file' }))

const modalContent = openModal.mock.calls[0][0].content as ReactElement<{
isFile?: boolean
}>

expect(modalContent.props.isFile).toBe(true)
})

it('should call delete callback after deleting an external secret', async () => {
const deleteVariable = jest.fn()
const openModalConfirmation = jest.fn()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,36 @@ type ExternalSecretRow = {

const columnHelper = createColumnHelper<ExternalSecretRow>()

type AddSecretDropdownProps = {
color?: 'brand' | 'neutral'
onSelect: (isFile: boolean) => void
}

function AddSecretDropdown({ color = 'brand', onSelect }: AddSecretDropdownProps) {
return (
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>
<Button color={color} variant="solid" size="md" type="button">
<Icon iconName="circle-plus" iconStyle="regular" />
Add secret
<Icon iconName="angle-down" />
</Button>
</DropdownMenu.Trigger>
<DropdownMenu.Content className="w-[240px]">
{ADD_SECRET_OPTIONS.map((option) => (
<DropdownMenu.Item
key={option.value}
icon={<Icon iconName={option.icon} />}
onSelect={() => onSelect(option.value === 'file')}
>
{option.label}
</DropdownMenu.Item>
))}
</DropdownMenu.Content>
</DropdownMenu.Root>
)
}

export type ExternalSecretsTabProps = {
scope: VariableScope
parentId: string
Expand Down Expand Up @@ -308,19 +338,7 @@ export function ExternalSecretsTab({
title: 'No external secrets yet',
description: 'Add a secret or connect a secret manager to sync external secrets.',
icon: 'lock-keyhole' as const,
actions: (
<Button
color="neutral"
size="md"
variant="solid"
type="button"
disabled={!hasClusterSecretManagerConfigured}
onClick={() => handleOpenAddSecret(false)}
>
<Icon iconName="circle-plus" iconStyle="regular" />
Add secret
</Button>
),
actions: <AddSecretDropdown color="neutral" onSelect={handleOpenAddSecret} />,
}
}, [clusterId, handleOpenAddSecret, hasClusterSecretManagerConfigured, organizationId, secrets.length])

Expand Down Expand Up @@ -518,26 +536,7 @@ export function ExternalSecretsTab({
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>
<Button color="brand" variant="solid" size="md">
<Icon iconName="circle-plus" iconStyle="regular" />
Add secret
<Icon iconName="chevron-down" className="text-[10px]" />
</Button>
</DropdownMenu.Trigger>
<DropdownMenu.Content className="w-[240px]">
{ADD_SECRET_OPTIONS.map((option) => (
<DropdownMenu.Item
key={option.value}
icon={<Icon iconName={option.icon} />}
onSelect={() => handleOpenAddSecret(option.value === 'file')}
>
{option.label}
</DropdownMenu.Item>
))}
</DropdownMenu.Content>
</DropdownMenu.Root>
<AddSecretDropdown onSelect={handleOpenAddSecret} />
</div>
</div>
)}
Expand Down
Loading