@skyhook-io/k8s-ui 1.12.0 → 1.13.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/package.json +4 -4
  2. package/src/components/applications/ApplicationDetail.tsx +2 -2
  3. package/src/components/cluster-switcher/ClusterSwitcher.tsx +32 -7
  4. package/src/components/issues/severity.ts +1 -0
  5. package/src/components/resources/ResourcesView.empty-note.test.ts +41 -0
  6. package/src/components/resources/ResourcesView.tsx +419 -9
  7. package/src/components/resources/index.ts +27 -0
  8. package/src/components/resources/kyverno-legacy-action.test.ts +98 -0
  9. package/src/components/resources/renderers/AnalysisRunRenderer.test.tsx +241 -0
  10. package/src/components/resources/renderers/AnalysisRunRenderer.tsx +166 -0
  11. package/src/components/resources/renderers/CNPGBackupRenderer.tsx +31 -2
  12. package/src/components/resources/renderers/CNPGClusterRenderer.test.tsx +33 -0
  13. package/src/components/resources/renderers/CNPGClusterRenderer.tsx +47 -4
  14. package/src/components/resources/renderers/CNPGDeclarativeRenderer.tsx +243 -0
  15. package/src/components/resources/renderers/CNPGObjectStoreRenderer.test.tsx +99 -0
  16. package/src/components/resources/renderers/CNPGObjectStoreRenderer.tsx +235 -0
  17. package/src/components/resources/renderers/CNPGScheduledBackupRenderer.tsx +23 -1
  18. package/src/components/resources/renderers/CNPGShared.tsx +27 -0
  19. package/src/components/resources/renderers/CalicoHostEndpointRenderer.tsx +71 -0
  20. package/src/components/resources/renderers/CalicoIPPoolRenderer.tsx +32 -0
  21. package/src/components/resources/renderers/CalicoNetworkPolicyDiagram.test.tsx +205 -0
  22. package/src/components/resources/renderers/CalicoNetworkPolicyDiagram.tsx +537 -0
  23. package/src/components/resources/renderers/CalicoNetworkPolicyRenderer.test.tsx +223 -0
  24. package/src/components/resources/renderers/CalicoNetworkPolicyRenderer.tsx +670 -0
  25. package/src/components/resources/renderers/CalicoTierRenderer.tsx +19 -0
  26. package/src/components/resources/renderers/KyvernoCELPolicyRenderers.tsx +13 -6
  27. package/src/components/resources/renderers/KyvernoExceptionRenderers.tsx +3 -2
  28. package/src/components/resources/renderers/KyvernoGlobalContextRenderer.test.ts +31 -0
  29. package/src/components/resources/renderers/KyvernoGlobalContextRenderer.tsx +124 -0
  30. package/src/components/resources/renderers/KyvernoPolicyReportRenderer.tsx +28 -2
  31. package/src/components/resources/renderers/KyvernoQueueRenderers.tsx +303 -0
  32. package/src/components/resources/renderers/LookupFailureNote.test.tsx +60 -0
  33. package/src/components/resources/renderers/LookupFailureNote.tsx +42 -0
  34. package/src/components/resources/renderers/NamespaceRenderer.test.ts +30 -0
  35. package/src/components/resources/renderers/NamespaceRenderer.tsx +7 -3
  36. package/src/components/resources/renderers/NetworkPolicyDiagram.tsx +27 -26
  37. package/src/components/resources/renderers/NetworkPolicyRenderer.tsx +6 -3
  38. package/src/components/resources/renderers/PolicyCoverageSection.test.tsx +348 -0
  39. package/src/components/resources/renderers/PolicyCoverageSection.tsx +939 -0
  40. package/src/components/resources/renderers/PolicySection.test.tsx +59 -2
  41. package/src/components/resources/renderers/PolicySection.tsx +32 -5
  42. package/src/components/resources/renderers/RolloutRenderer.test.tsx +491 -0
  43. package/src/components/resources/renderers/RolloutRenderer.tsx +391 -92
  44. package/src/components/resources/renderers/VeleroBSLRenderer.test.tsx +374 -0
  45. package/src/components/resources/renderers/VeleroBSLRenderer.tsx +179 -3
  46. package/src/components/resources/renderers/VeleroBackupRenderer.test.tsx +221 -0
  47. package/src/components/resources/renderers/VeleroBackupRenderer.tsx +119 -6
  48. package/src/components/resources/renderers/VeleroBackupRepositoryRenderer.test.tsx +69 -0
  49. package/src/components/resources/renderers/VeleroBackupRepositoryRenderer.tsx +96 -0
  50. package/src/components/resources/renderers/VeleroRestoreRenderer.test.tsx +55 -0
  51. package/src/components/resources/renderers/VeleroRestoreRenderer.tsx +22 -4
  52. package/src/components/resources/renderers/VeleroRunMessages.test.tsx +152 -0
  53. package/src/components/resources/renderers/VeleroRunMessages.tsx +139 -0
  54. package/src/components/resources/renderers/VeleroScheduleRenderer.test.tsx +88 -0
  55. package/src/components/resources/renderers/VeleroScheduleRenderer.tsx +16 -6
  56. package/src/components/resources/renderers/calico-cells.tsx +177 -0
  57. package/src/components/resources/renderers/cnpg-cells.tsx +100 -0
  58. package/src/components/resources/renderers/index.ts +16 -1
  59. package/src/components/resources/renderers/kyverno-cells.tsx +81 -0
  60. package/src/components/resources/renderers/network-policy-peer-styles.ts +25 -0
  61. package/src/components/resources/renderers/velero-cells.tsx +29 -7
  62. package/src/components/resources/renderers/velero-dead-ends.test.tsx +142 -0
  63. package/src/components/resources/resource-utils-calico.test.ts +215 -0
  64. package/src/components/resources/resource-utils-calico.ts +261 -0
  65. package/src/components/resources/resource-utils-cnpg-declarative.test.ts +104 -0
  66. package/src/components/resources/resource-utils-cnpg-objectstore.test.ts +122 -0
  67. package/src/components/resources/resource-utils-cnpg.test.ts +54 -0
  68. package/src/components/resources/resource-utils-cnpg.ts +264 -3
  69. package/src/components/resources/resource-utils-kyverno-modern.ts +24 -0
  70. package/src/components/resources/resource-utils-kyverno-queue.test.ts +164 -0
  71. package/src/components/resources/resource-utils-kyverno-queue.ts +198 -0
  72. package/src/components/resources/resource-utils-kyverno.ts +43 -7
  73. package/src/components/resources/resource-utils-velero.test.ts +26 -0
  74. package/src/components/resources/resource-utils-velero.ts +68 -0
  75. package/src/components/resources/resource-utils.ts +98 -0
  76. package/src/components/shared/ResourceActionsBar.tsx +101 -16
  77. package/src/components/shared/ResourceRendererDispatch.test.tsx +379 -3
  78. package/src/components/shared/ResourceRendererDispatch.tsx +199 -22
  79. package/src/components/shared/index.ts +1 -1
  80. package/src/components/shared/revision-role-badges.test.ts +120 -0
  81. package/src/components/topology/K8sResourceNode.test.ts +10 -0
  82. package/src/components/topology/K8sResourceNode.tsx +22 -0
  83. package/src/components/topology/TopologyFilterSidebar.test.tsx +31 -0
  84. package/src/components/topology/TopologyFilterSidebar.tsx +5 -0
  85. package/src/components/topology/TopologyGraph.tsx +6 -6
  86. package/src/components/topology/TopologySearch.tsx +7 -0
  87. package/src/components/topology/layout.ts +3 -0
  88. package/src/components/trace/ReachabilityView.tsx +1 -0
  89. package/src/components/trace/TracePanel.tsx +13 -2
  90. package/src/components/trace/reachFixtures.ts +26 -1
  91. package/src/components/trace/reachGraphModel.test.ts +20 -0
  92. package/src/components/trace/reachGraphModel.ts +8 -1
  93. package/src/components/trace/reachOrigins.test.ts +31 -0
  94. package/src/components/trace/reachOrigins.ts +11 -1
  95. package/src/components/ui/Badge.tsx +10 -0
  96. package/src/components/ui/InClusterConsentDialog.tsx +5 -4
  97. package/src/components/ui/ScheduleValue.test.tsx +80 -0
  98. package/src/components/ui/ScheduleValue.tsx +85 -0
  99. package/src/components/ui/SelectMenu.tsx +202 -28
  100. package/src/components/ui/drawer-components.test.tsx +17 -1
  101. package/src/components/ui/drawer-components.tsx +9 -2
  102. package/src/components/ui/index.ts +2 -0
  103. package/src/components/workload/WorkloadView.tsx +5 -2
  104. package/src/types/core.ts +36 -4
  105. package/src/types/policy.ts +153 -2
  106. package/src/utils/clipboard.test.ts +86 -0
  107. package/src/utils/clipboard.ts +50 -0
  108. package/src/utils/extended-resources.ts +4 -7
  109. package/src/utils/format.test.ts +83 -1
  110. package/src/utils/format.ts +80 -1
  111. package/src/utils/inClusterConsent.test.ts +9 -0
  112. package/src/utils/inClusterConsent.ts +9 -9
  113. package/src/utils/index.ts +1 -0
  114. package/src/utils/navigation.test.ts +5 -0
  115. package/src/utils/navigation.ts +5 -0
  116. package/src/utils/resource-icons.test.ts +49 -2
  117. package/src/utils/resource-icons.ts +29 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyhook-io/k8s-ui",
3
- "version": "1.12.0",
3
+ "version": "1.13.3",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/skyhook-io/radar",
@@ -71,7 +71,7 @@
71
71
  "html-to-image": "^1.11.0",
72
72
  "monaco-yaml": "5.5.1",
73
73
  "react-virtuoso": "^4.18.11",
74
- "shiki": "^4.2.0"
74
+ "shiki": "^4.4.2"
75
75
  },
76
76
  "peerDependencies": {
77
77
  "@xterm/addon-fit": ">=0.10.0",
@@ -88,7 +88,7 @@
88
88
  "yaml": ">=2.0.0"
89
89
  },
90
90
  "devDependencies": {
91
- "@types/node": "^26.1.1",
91
+ "@types/node": "^26.2.0",
92
92
  "@types/react": "^19.2.18",
93
93
  "@types/react-dom": "^19.2.3",
94
94
  "@xterm/addon-fit": "^0.11.0",
@@ -98,7 +98,7 @@
98
98
  "clsx": "^2.1.1",
99
99
  "diff": "^9.0.0",
100
100
  "elkjs": "^0.11.1",
101
- "lucide-react": "^1.28.0",
101
+ "lucide-react": "^1.33.0",
102
102
  "monaco-editor": "0.55.1",
103
103
  "react": "^19.2.8",
104
104
  "react-dom": "^19.2.8",
@@ -13,7 +13,7 @@ import {
13
13
  Boxes,
14
14
  ChevronDown,
15
15
  Clock3,
16
- DollarSign,
16
+ Coins,
17
17
  ExternalLink,
18
18
  GitCommit,
19
19
  Layers,
@@ -988,7 +988,7 @@ function ApplicationViewTabs({
988
988
  : "border-transparent text-theme-text-secondary hover:border-theme-border-light hover:text-theme-text-primary",
989
989
  )}
990
990
  >
991
- <DollarSign className="h-4 w-4" />
991
+ <Coins className="h-4 w-4" />
992
992
  Cost
993
993
  </button>
994
994
  )}
@@ -18,6 +18,9 @@ export interface ClusterSwitcherItem {
18
18
  /** Raw context / display string. ClusterName collapses GKE/EKS/AKS
19
19
  * shapes; user-named clusters pass through unchanged. */
20
20
  name: string
21
+ /** Short qualifier rendered after the parsed name without affecting
22
+ * provider detection, e.g. a source suffix for colliding contexts. */
23
+ nameQualifier?: string
21
24
  secondary?: string
22
25
  badge?: string
23
26
  /** Origin label, rendered as a folder-icon line under the name.
@@ -44,6 +47,8 @@ export interface ClusterSwitcherProps {
44
47
  /** Raw context / display string. Pass it as-is — the trigger renders
45
48
  * through ClusterName, which handles parse + provider badge + tooltip. */
46
49
  currentName: string
50
+ /** Trigger-side counterpart to {@link ClusterSwitcherItem.nameQualifier}. */
51
+ currentNameQualifier?: string
47
52
  /** Trigger-side counterpart to {@link ClusterSwitcherItem.sourceLabel}.
48
53
  * Only pass when 2+ kubeconfig sources are loaded. */
49
54
  currentSourceLabel?: string
@@ -80,6 +85,7 @@ const TRIGGER_NAME_MAX_WIDTH = 'max-w-[140px] sm:max-w-[220px] xl:max-w-[340px]'
80
85
  export const ClusterSwitcher = forwardRef<ClusterSwitcherHandle, ClusterSwitcherProps>(({
81
86
  currentId,
82
87
  currentName,
88
+ currentNameQualifier,
83
89
  currentSourceLabel,
84
90
  items,
85
91
  onSelect,
@@ -114,6 +120,7 @@ export const ClusterSwitcher = forwardRef<ClusterSwitcherHandle, ClusterSwitcher
114
120
  if (!q) return true
115
121
  return (
116
122
  item.name.toLowerCase().includes(q) ||
123
+ item.nameQualifier?.toLowerCase().includes(q) ||
117
124
  item.secondary?.toLowerCase().includes(q) ||
118
125
  item.badge?.toLowerCase().includes(q) ||
119
126
  item.sourceLabel?.toLowerCase().includes(q) ||
@@ -209,7 +216,7 @@ export const ClusterSwitcher = forwardRef<ClusterSwitcherHandle, ClusterSwitcher
209
216
  disabled={disabled || loading}
210
217
  className={
211
218
  variant === 'segment'
212
- ? `flex items-center gap-1.5 px-3 py-1.5 h-full min-w-[150px] max-w-[264px] text-[13px] font-medium
219
+ ? `flex items-center gap-1.5 px-3 py-1.5 h-full min-w-[150px] max-w-[264px] overflow-hidden text-[13px] font-medium
213
220
  text-theme-text-primary hover:bg-theme-hover transition-colors cursor-pointer
214
221
  disabled:opacity-50 disabled:cursor-not-allowed`
215
222
  : `flex items-center gap-1.5 px-2.5 py-1.5 min-w-[140px]
@@ -236,12 +243,22 @@ export const ClusterSwitcher = forwardRef<ClusterSwitcherHandle, ClusterSwitcher
236
243
  // context inline (per-row secondary line), and an extra hover
237
244
  // tooltip would just overlap the search input.
238
245
  <>
239
- <ClusterName
240
- name={currentName}
241
- fallbackBadge={<Server className="w-3.5 h-3.5 text-theme-text-secondary" />}
242
- className={variant === 'segment' ? 'min-w-0 max-w-[214px]' : TRIGGER_NAME_MAX_WIDTH}
243
- noTooltip={isOpen}
244
- />
246
+ <span className={variant === 'segment' ? 'min-w-0 flex-1' : 'min-w-0'}>
247
+ <ClusterName
248
+ name={currentName}
249
+ fallbackBadge={<Server className="w-3.5 h-3.5 text-theme-text-secondary" />}
250
+ className={variant === 'segment' ? 'min-w-0 max-w-full' : TRIGGER_NAME_MAX_WIDTH}
251
+ noTooltip={isOpen}
252
+ />
253
+ </span>
254
+ {currentNameQualifier && (
255
+ <span
256
+ className="shrink-0 max-w-[80px] truncate text-xs font-normal text-theme-text-tertiary"
257
+ title={currentNameQualifier}
258
+ >
259
+ {currentNameQualifier}
260
+ </span>
261
+ )}
245
262
  {currentSourceLabel && (
246
263
  // Icon-only on the trigger: long folder paths (the very case
247
264
  // that motivates the chip) middle-truncate to something
@@ -358,6 +375,14 @@ export const ClusterSwitcher = forwardRef<ClusterSwitcherHandle, ClusterSwitcher
358
375
  : 'text-theme-text-primary'
359
376
  }`}
360
377
  />
378
+ {item.nameQualifier && (
379
+ <span
380
+ className="shrink-0 max-w-[120px] truncate text-xs font-normal text-theme-text-tertiary"
381
+ title={item.nameQualifier}
382
+ >
383
+ {item.nameQualifier}
384
+ </span>
385
+ )}
361
386
  {item.badge && (
362
387
  <span className="shrink-0 text-[10px] text-theme-text-tertiary bg-theme-elevated px-1 rounded">
363
388
  {item.badge}
@@ -85,6 +85,7 @@ const CATEGORY_LABEL: Record<string, string> = {
85
85
  volume_access_mode_conflict: 'Volume access conflict',
86
86
  backup_failed: 'Backup failed',
87
87
  backup_target_unavailable: 'Backup target unavailable',
88
+ backup_stalled: 'Backup not progressing',
88
89
  job_failed: 'Job failed',
89
90
  cronjob_failed: 'CronJob failed',
90
91
  rollout_stalled: 'Rollout stalled',
@@ -0,0 +1,41 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { emptyKindNote } from './ResourcesView'
3
+
4
+ describe('emptyKindNote', () => {
5
+ it('explains that no UpdateRequest is the resting state, and that it looks the same as a rule that never ran', () => {
6
+ const note = emptyKindNote('UpdateRequest', 'kyverno.io')
7
+ expect(note).toContain('deleted seconds after')
8
+ expect(note).toContain('never ran')
9
+ })
10
+
11
+ it('says an absent EphemeralReport means the scan finished, not that nothing was checked', () => {
12
+ expect(emptyKindNote('EphemeralReport', 'reports.kyverno.io')).toContain('scan has finished')
13
+ expect(emptyKindNote('ClusterEphemeralReport', 'reports.kyverno.io')).toContain('scan has finished')
14
+ })
15
+
16
+ it('is case-insensitive on the kind, which arrives from a URL as often as from discovery', () => {
17
+ expect(emptyKindNote('updaterequest', 'kyverno.io')).toBeDefined()
18
+ })
19
+
20
+ // Same plural, different operator: Kyverno's group is what makes the note true.
21
+ it('says nothing about a kind of the same name from another group', () => {
22
+ expect(emptyKindNote('UpdateRequest', 'example.com')).toBeUndefined()
23
+ expect(emptyKindNote('EphemeralReport', '')).toBeUndefined()
24
+ })
25
+
26
+ it('says nothing about kinds with no such story', () => {
27
+ expect(emptyKindNote('Pod', '')).toBeUndefined()
28
+ })
29
+
30
+ // The table can be empty because the reader emptied it. Telling them none is
31
+ // normal then answers a question they did not ask, with their term still in
32
+ // the search box.
33
+ it('stays silent when the kind has rows and a filter is what emptied the table', () => {
34
+ expect(emptyKindNote('UpdateRequest', 'kyverno.io', 3)).toBeUndefined()
35
+ expect(emptyKindNote('EphemeralReport', 'reports.kyverno.io', 192)).toBeUndefined()
36
+ })
37
+
38
+ it('still speaks when the kind is genuinely empty', () => {
39
+ expect(emptyKindNote('UpdateRequest', 'kyverno.io', 0)).toBeDefined()
40
+ })
41
+ })