feat(environments): migrate KubeConfigInfo to React (PR 8 of 10) [BE-12524] (#1625)

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Chaim Lev-Ari
2026-01-09 12:37:21 +00:00
committed by GitHub
parent 36296d2f5d
commit be44eedeb8
5 changed files with 206 additions and 18 deletions

View File

@@ -8,9 +8,10 @@ import { TagsDatatable } from '@/react/portainer/environments/TagsView/TagsDatat
import { AzureEndpointConfigSection } from '@/react/portainer/environments/ItemView/AzureEndpointConfigSection/AzureEndpointConfigSection';
import { EnvironmentBasicConfigSection } from '@/react/portainer/environments/ItemView/EnvironmentBasicConfigSection/EnvironmentBasicConfigSection';
import { EdgeInformationPanel } from '@/react/portainer/environments/ItemView/EdgeInformationPanel/EdgeInformationPanel';
import { withUIRouter } from '@/react-tools/withUIRouter';
import { KubeConfigInfo } from '@/react/portainer/environments/ItemView/KubeConfigInfo/KubeConfigInfo';
import { withReactQuery } from '@/react-tools/withReactQuery';
import { withCurrentUser } from '@/react-tools/withCurrentUser';
import { withUIRouter } from '@/react-tools/withUIRouter';
export const environmentsModule = angular
.module('portainer.app.react.components.environments', [])
@@ -23,6 +24,15 @@ export const environmentsModule = angular
'asyncMode',
])
)
.component(
'kubeConfigInfo',
r2a(withUIRouter(withReactQuery(KubeConfigInfo)), [
'environmentId',
'environmentType',
'edgeId',
'status',
])
)
.component('kvmControl', r2a(KVMControl, ['deviceId', 'server', 'token']))
.component('tagsDatatable', r2a(TagsDatatable, ['dataset', 'onRemove']))
.component(

View File

@@ -19,15 +19,9 @@
</div>
</div>
<information-panel ng-if="state.kubernetesEndpoint && (!state.edgeEndpoint || state.edgeAssociated)" title-text="Kubernetes features configuration">
<span class="small text-muted vertical-center">
<pr-icon icon="'wrench'" mode="'primary'"></pr-icon>
<div>
You should configure the features available in this Kubernetes environment in the
<a ui-sref="kubernetes.cluster.setup({endpointId: endpoint.Id})">Kubernetes configuration</a> view.
</div>
</span>
</information-panel>
<div class="mx-4 space-y-4 [&>*]:block mb-4">
<kube-config-info environment-id="endpoint.Id" environment-type="endpoint.Type" edge-id="endpoint.EdgeID" status="endpoint.Status"></kube-config-info>
</div>
</div>
<div class="row mt-4">

View File

@@ -1,7 +1,7 @@
import { FormControl } from '@@/form-components/FormControl';
import { FormSection } from '@@/form-components/FormSection';
import { Input } from '@@/form-components/Input';
import { Icon } from '@@/Icon';
import { TextTip } from '@@/Tip/TextTip';
interface Values {
name: string;
@@ -99,15 +99,10 @@ export function EnvironmentBasicConfigSection({
</FormControl>
{isEdge && (
<div className="col-sm-12 small text-muted vertical-center mt-2">
<Icon
icon="alert-circle"
mode="primary"
className="space-right"
/>
<TextTip color="blue">
Use https connection on Edge agent to use private registries
with credentials.
</div>
</TextTip>
)}
</>
)}

View File

@@ -0,0 +1,132 @@
import { render, screen } from '@testing-library/react';
import {
EnvironmentType,
EnvironmentStatus,
} from '@/react/portainer/environments/types';
import { withTestRouter } from '@/react/test-utils/withRouter';
import { KubeConfigInfo } from './KubeConfigInfo';
function renderPanel(
environmentType: EnvironmentType,
status: EnvironmentStatus = EnvironmentStatus.Up,
edgeId?: string
) {
const Wrapped = withTestRouter(KubeConfigInfo);
return render(
<Wrapped
environmentId={1}
environmentType={environmentType}
status={status}
edgeId={edgeId}
/>
);
}
describe('KubeConfigInfo', () => {
describe('Visibility', () => {
it('should display for KubernetesLocal environment', () => {
renderPanel(EnvironmentType.KubernetesLocal);
expect(
screen.getByText('Kubernetes features configuration')
).toBeVisible();
expect(screen.getByText('Kubernetes configuration')).toBeVisible();
});
it('should display for AgentOnKubernetes environment', () => {
renderPanel(EnvironmentType.AgentOnKubernetes);
expect(
screen.getByText('Kubernetes features configuration')
).toBeVisible();
});
it('should display for EdgeAgentOnKubernetes with EdgeID', () => {
renderPanel(
EnvironmentType.EdgeAgentOnKubernetes,
EnvironmentStatus.Up,
'edge-123'
);
expect(
screen.getByText('Kubernetes features configuration')
).toBeVisible();
});
it('should not display for EdgeAgentOnKubernetes without EdgeID', () => {
renderPanel(EnvironmentType.EdgeAgentOnKubernetes);
expect(
screen.queryByText('Kubernetes features configuration')
).not.toBeInTheDocument();
});
it('should not display when status is Down', () => {
renderPanel(EnvironmentType.KubernetesLocal, EnvironmentStatus.Down);
expect(
screen.queryByText('Kubernetes features configuration')
).not.toBeInTheDocument();
});
it('should not display for Docker environment', () => {
renderPanel(EnvironmentType.Docker);
expect(
screen.queryByText('Kubernetes features configuration')
).not.toBeInTheDocument();
});
it('should not display for Azure environment', () => {
renderPanel(EnvironmentType.Azure);
expect(
screen.queryByText('Kubernetes features configuration')
).not.toBeInTheDocument();
});
});
describe('Content', () => {
it('should display wrench icon', () => {
const { container } = renderPanel(EnvironmentType.KubernetesLocal);
const icon = container.querySelector('svg');
expect(icon).toBeInTheDocument();
});
it('should display correct title', () => {
renderPanel(EnvironmentType.KubernetesLocal);
expect(
screen.getByText('Kubernetes features configuration')
).toBeVisible();
});
it('should display correct message text', () => {
renderPanel(EnvironmentType.KubernetesLocal);
expect(
screen.getByText(/You should configure the features available/i)
).toBeVisible();
});
it('should display link with correct text', () => {
renderPanel(EnvironmentType.KubernetesLocal);
const link = screen.getByTestId('kubernetes-config-link');
expect(link).toBeVisible();
expect(link).toHaveTextContent('Kubernetes configuration');
});
it('should have link element', () => {
renderPanel(EnvironmentType.KubernetesLocal);
const link = screen.getByTestId('kubernetes-config-link');
// Link component renders an anchor tag
expect(link.tagName).toBe('A');
});
});
});

View File

@@ -0,0 +1,57 @@
import { Wrench } from 'lucide-react';
import {
EnvironmentId,
EnvironmentType,
EnvironmentStatus,
} from '@/react/portainer/environments/types';
import {
isKubernetesEnvironment,
isEdgeEnvironment,
} from '@/react/portainer/environments/utils';
import { InformationPanel } from '@/react/components/InformationPanel';
import { Link } from '@/react/components/Link';
import { Icon } from '@/react/components/Icon';
interface Props {
environmentId: EnvironmentId;
environmentType: EnvironmentType;
edgeId?: string;
status: EnvironmentStatus;
}
export function KubeConfigInfo({
environmentId,
environmentType,
edgeId,
status,
}: Props) {
const isVisible =
isKubernetesEnvironment(environmentType) &&
(!isEdgeEnvironment(environmentType) || !!edgeId) &&
status !== EnvironmentStatus.Down;
if (!isVisible) {
return null;
}
return (
<InformationPanel title="Kubernetes features configuration">
<span className="small text-muted vertical-center">
<Icon icon={Wrench} mode="primary" />
<div>
You should configure the features available in this Kubernetes
environment in the{' '}
<Link
to="kubernetes.cluster.setup"
params={{ endpointId: environmentId }}
data-cy="kubernetes-config-link"
>
Kubernetes configuration
</Link>{' '}
view.
</div>
</span>
</InformationPanel>
);
}