fix: resolve TypeScript compilation errors and failing tests
- Update registerDetailsViewSection and registerAppBarAction calls to match new Headlamp plugin API (single argument) - Add SimpleTable mock to DashboardView tests - Fix all TypeScript compilation errors - All 50 tests now passing Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
This commit is contained in:
@@ -34,6 +34,17 @@ vi.mock('@kinvolk/headlamp-plugin/lib/CommonComponents', () => ({
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
),
|
),
|
||||||
|
SimpleTable: ({ data }: { data: Array<any> }) => (
|
||||||
|
<table data-testid="simple-table">
|
||||||
|
<tbody>
|
||||||
|
{data.map((item, idx) => (
|
||||||
|
<tr key={idx}>
|
||||||
|
<td>{JSON.stringify(item)}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
),
|
||||||
PercentageCircle: ({ label }: { label: string }) => (
|
PercentageCircle: ({ label }: { label: string }) => (
|
||||||
<div data-testid="percentage-circle">{label}</div>
|
<div data-testid="percentage-circle">{label}</div>
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -100,19 +100,21 @@ export default function DashboardView() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '20px' }}>
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginBottom: '20px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<SectionHeader title="Polaris — Overview" />
|
<SectionHeader title="Polaris — Overview" />
|
||||||
{data && (
|
{data && (
|
||||||
<div style={{ display: 'flex', gap: '16px', alignItems: 'center' }}>
|
<div style={{ display: 'flex', gap: '16px', alignItems: 'center' }}>
|
||||||
<span style={{ fontSize: '14px', color: 'var(--mui-palette-text-secondary, #666)' }}>
|
<span style={{ fontSize: '14px', color: 'var(--mui-palette-text-secondary, #666)' }}>
|
||||||
Last updated: {formatAuditTime(data.AuditTime)}
|
Last updated: {formatAuditTime(data.AuditTime)}
|
||||||
</span>
|
</span>
|
||||||
<Button
|
<Button variant="outlined" startIcon={<RefreshIcon />} onClick={refresh} size="small">
|
||||||
variant="outlined"
|
|
||||||
startIcon={<RefreshIcon />}
|
|
||||||
onClick={refresh}
|
|
||||||
size="small"
|
|
||||||
>
|
|
||||||
Refresh
|
Refresh
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -21,7 +21,12 @@ interface CheckFailure {
|
|||||||
* Exemption management UI for adding/removing Polaris exemptions
|
* Exemption management UI for adding/removing Polaris exemptions
|
||||||
* Uses annotation patches on the workload resource
|
* Uses annotation patches on the workload resource
|
||||||
*/
|
*/
|
||||||
export default function ExemptionManager({ workloadResult, namespace, kind, name }: ExemptionManagerProps) {
|
export default function ExemptionManager({
|
||||||
|
workloadResult,
|
||||||
|
namespace,
|
||||||
|
kind,
|
||||||
|
name,
|
||||||
|
}: ExemptionManagerProps) {
|
||||||
const [dialogOpen, setDialogOpen] = React.useState(false);
|
const [dialogOpen, setDialogOpen] = React.useState(false);
|
||||||
const [selectedChecks, setSelectedChecks] = React.useState<Set<string>>(new Set());
|
const [selectedChecks, setSelectedChecks] = React.useState<Set<string>>(new Set());
|
||||||
const [exemptAll, setExemptAll] = React.useState(false);
|
const [exemptAll, setExemptAll] = React.useState(false);
|
||||||
@@ -169,18 +174,11 @@ export default function ExemptionManager({ workloadResult, namespace, kind, name
|
|||||||
</Button>
|
</Button>
|
||||||
</SectionBox>
|
</SectionBox>
|
||||||
|
|
||||||
<Dialog
|
<Dialog open={dialogOpen} onClose={() => setDialogOpen(false)} title="Add Exemptions">
|
||||||
open={dialogOpen}
|
|
||||||
onClose={() => setDialogOpen(false)}
|
|
||||||
title="Add Exemptions"
|
|
||||||
>
|
|
||||||
<div style={{ padding: '16px', minWidth: '400px' }}>
|
<div style={{ padding: '16px', minWidth: '400px' }}>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox checked={exemptAll} onChange={e => setExemptAll(e.target.checked)} />
|
||||||
checked={exemptAll}
|
|
||||||
onChange={(e) => setExemptAll(e.target.checked)}
|
|
||||||
/>
|
|
||||||
}
|
}
|
||||||
label="Exempt from all checks"
|
label="Exempt from all checks"
|
||||||
/>
|
/>
|
||||||
@@ -207,10 +205,10 @@ export default function ExemptionManager({ workloadResult, namespace, kind, name
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div style={{ marginTop: '16px', display: 'flex', gap: '8px', justifyContent: 'flex-end' }}>
|
<div
|
||||||
<Button onClick={() => setDialogOpen(false)}>
|
style={{ marginTop: '16px', display: 'flex', gap: '8px', justifyContent: 'flex-end' }}
|
||||||
Cancel
|
>
|
||||||
</Button>
|
<Button onClick={() => setDialogOpen(false)}>Cancel</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={applyExemptions}
|
onClick={applyExemptions}
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
import { NameValueTable, SectionBox, StatusLabel, SimpleTable } from '@kinvolk/headlamp-plugin/lib/CommonComponents';
|
import {
|
||||||
|
NameValueTable,
|
||||||
|
SectionBox,
|
||||||
|
StatusLabel,
|
||||||
|
SimpleTable,
|
||||||
|
} from '@kinvolk/headlamp-plugin/lib/CommonComponents';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { usePolarisDataContext } from '../api/PolarisDataContext';
|
import { usePolarisDataContext } from '../api/PolarisDataContext';
|
||||||
@@ -140,9 +145,7 @@ export default function InlineAuditSection({ resource }: InlineAuditSectionProps
|
|||||||
{
|
{
|
||||||
label: 'Severity',
|
label: 'Severity',
|
||||||
getter: (f: CheckFailure) => (
|
getter: (f: CheckFailure) => (
|
||||||
<StatusLabel status={getSeverityStatus(f.severity)}>
|
<StatusLabel status={getSeverityStatus(f.severity)}>{f.severity}</StatusLabel>
|
||||||
{f.severity}
|
|
||||||
</StatusLabel>
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{ label: 'Message', getter: (f: CheckFailure) => f.message },
|
{ label: 'Message', getter: (f: CheckFailure) => f.message },
|
||||||
@@ -153,7 +156,10 @@ export default function InlineAuditSection({ resource }: InlineAuditSectionProps
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div style={{ marginTop: '16px' }}>
|
<div style={{ marginTop: '16px' }}>
|
||||||
<Link to={`/polaris/namespaces#${namespace}`} style={{ color: 'var(--link-color, #1976d2)' }}>
|
<Link
|
||||||
|
to={`/polaris/namespaces#${namespace}`}
|
||||||
|
style={{ color: 'var(--link-color, #1976d2)' }}
|
||||||
|
>
|
||||||
View Full Report →
|
View Full Report →
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -118,7 +118,11 @@ function NamespaceDetailPanel({ namespace, onClose }: NamespaceDetailPanelProps)
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<h2 style={{ margin: 0, color: 'var(--mui-palette-text-primary, var(--text-primary, #000))' }}>Polaris — {namespace}</h2>
|
<h2
|
||||||
|
style={{ margin: 0, color: 'var(--mui-palette-text-primary, var(--text-primary, #000))' }}
|
||||||
|
>
|
||||||
|
Polaris — {namespace}
|
||||||
|
</h2>
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -1,8 +1,19 @@
|
|||||||
import { NameValueTable, SectionBox, StatusLabel } from '@kinvolk/headlamp-plugin/lib/CommonComponents';
|
import {
|
||||||
|
NameValueTable,
|
||||||
|
SectionBox,
|
||||||
|
StatusLabel,
|
||||||
|
} from '@kinvolk/headlamp-plugin/lib/CommonComponents';
|
||||||
import { ApiProxy } from '@kinvolk/headlamp-plugin/lib';
|
import { ApiProxy } from '@kinvolk/headlamp-plugin/lib';
|
||||||
import { Button } from '@mui/material';
|
import { Button } from '@mui/material';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { getDashboardUrl, getRefreshInterval, INTERVAL_OPTIONS, setDashboardUrl, setRefreshInterval, AuditData } from '../api/polaris';
|
import {
|
||||||
|
getDashboardUrl,
|
||||||
|
getRefreshInterval,
|
||||||
|
INTERVAL_OPTIONS,
|
||||||
|
setDashboardUrl,
|
||||||
|
setRefreshInterval,
|
||||||
|
AuditData,
|
||||||
|
} from '../api/polaris';
|
||||||
|
|
||||||
interface PluginSettingsProps {
|
interface PluginSettingsProps {
|
||||||
data?: { [key: string]: string | number | boolean };
|
data?: { [key: string]: string | number | boolean };
|
||||||
@@ -14,7 +25,9 @@ export default function PolarisSettings(props: PluginSettingsProps) {
|
|||||||
const currentInterval = (data?.refreshInterval as number) ?? getRefreshInterval();
|
const currentInterval = (data?.refreshInterval as number) ?? getRefreshInterval();
|
||||||
const currentUrl = (data?.dashboardUrl as string) ?? getDashboardUrl();
|
const currentUrl = (data?.dashboardUrl as string) ?? getDashboardUrl();
|
||||||
const [testing, setTesting] = React.useState(false);
|
const [testing, setTesting] = React.useState(false);
|
||||||
const [testResult, setTestResult] = React.useState<{ success: boolean; message: string } | null>(null);
|
const [testResult, setTestResult] = React.useState<{ success: boolean; message: string } | null>(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
function handleIntervalChange(e: React.ChangeEvent<HTMLSelectElement>) {
|
function handleIntervalChange(e: React.ChangeEvent<HTMLSelectElement>) {
|
||||||
const seconds = Number(e.target.value);
|
const seconds = Number(e.target.value);
|
||||||
@@ -51,7 +64,9 @@ export default function PolarisSettings(props: PluginSettingsProps) {
|
|||||||
|
|
||||||
setTestResult({
|
setTestResult({
|
||||||
success: true,
|
success: true,
|
||||||
message: `Connected successfully! Version: ${result.PolarisOutputVersion}, Last audit: ${new Date(result.AuditTime).toLocaleString()}`,
|
message: `Connected successfully! Version: ${
|
||||||
|
result.PolarisOutputVersion
|
||||||
|
}, Last audit: ${new Date(result.AuditTime).toLocaleString()}`,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setTestResult({
|
setTestResult({
|
||||||
@@ -97,9 +112,10 @@ export default function PolarisSettings(props: PluginSettingsProps) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div style={{ fontSize: '12px', color: '#666', marginTop: '4px' }}>
|
<div style={{ fontSize: '12px', color: '#666', marginTop: '4px' }}>
|
||||||
Examples:<br />
|
Examples:
|
||||||
• K8s proxy: <code>/api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy/</code><br />
|
<br />• K8s proxy:{' '}
|
||||||
• Full URL: <code>https://my-polaris.example.com</code>
|
<code>/api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy/</code>
|
||||||
|
<br />• Full URL: <code>https://my-polaris.example.com</code>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
|
|||||||
+2
-2
@@ -69,7 +69,7 @@ registerRoute({
|
|||||||
registerPluginSettings('polaris', PolarisSettings);
|
registerPluginSettings('polaris', PolarisSettings);
|
||||||
|
|
||||||
// Register details view section for supported controller types
|
// Register details view section for supported controller types
|
||||||
registerDetailsViewSection('polaris-audit', ({ resource }) => {
|
registerDetailsViewSection(({ resource }) => {
|
||||||
const supportedKinds = ['Deployment', 'StatefulSet', 'DaemonSet', 'Job', 'CronJob'];
|
const supportedKinds = ['Deployment', 'StatefulSet', 'DaemonSet', 'Job', 'CronJob'];
|
||||||
|
|
||||||
if (!supportedKinds.includes(resource?.kind)) {
|
if (!supportedKinds.includes(resource?.kind)) {
|
||||||
@@ -84,7 +84,7 @@ registerDetailsViewSection('polaris-audit', ({ resource }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Register app bar score badge
|
// Register app bar score badge
|
||||||
registerAppBarAction('polaris-score', () => (
|
registerAppBarAction(() => (
|
||||||
<PolarisDataProvider>
|
<PolarisDataProvider>
|
||||||
<AppBarScoreBadge />
|
<AppBarScoreBadge />
|
||||||
</PolarisDataProvider>
|
</PolarisDataProvider>
|
||||||
|
|||||||
Reference in New Issue
Block a user