@skyhook-io/k8s-ui 1.5.10 → 1.5.11

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 (39) hide show
  1. package/package.json +1 -1
  2. package/src/components/dock/BottomDock.tsx +21 -10
  3. package/src/components/resources/get-pod-phase-display.test.ts +254 -0
  4. package/src/components/resources/renderers/AWSManagedMachinePoolRenderer.tsx +2 -4
  5. package/src/components/resources/renderers/ArgoApplicationRenderer.tsx +3 -2
  6. package/src/components/resources/renderers/AzureManagedMachinePoolRenderer.tsx +3 -8
  7. package/src/components/resources/renderers/ChallengeRenderer.tsx +2 -1
  8. package/src/components/resources/renderers/ClusterComplianceReportRenderer.tsx +2 -1
  9. package/src/components/resources/renderers/ConfigAuditReportRenderer.tsx +2 -1
  10. package/src/components/resources/renderers/ExposedSecretReportRenderer.tsx +2 -1
  11. package/src/components/resources/renderers/GatewayClassRenderer.tsx +2 -1
  12. package/src/components/resources/renderers/GatewayRenderer.tsx +5 -4
  13. package/src/components/resources/renderers/IngressClassRenderer.tsx +2 -1
  14. package/src/components/resources/renderers/IstioAuthorizationPolicyRenderer.tsx +2 -1
  15. package/src/components/resources/renderers/IstioGatewayRenderer.tsx +3 -2
  16. package/src/components/resources/renderers/IstioPeerAuthenticationRenderer.tsx +2 -1
  17. package/src/components/resources/renderers/KarpenterNodeClaimRenderer.tsx +2 -2
  18. package/src/components/resources/renderers/OrderRenderer.tsx +2 -1
  19. package/src/components/resources/renderers/PodRenderer.tsx +23 -2
  20. package/src/components/resources/renderers/RoleBindingRenderer.tsx +3 -2
  21. package/src/components/resources/renderers/RolloutRenderer.tsx +2 -1
  22. package/src/components/resources/renderers/SecretRenderer.tsx +15 -6
  23. package/src/components/resources/renderers/aws-capi-cells.tsx +2 -4
  24. package/src/components/resources/renderers/azure-capi-cells.tsx +3 -8
  25. package/src/components/resources/renderers/istio-cells.tsx +3 -2
  26. package/src/components/resources/renderers/karpenter-cells.tsx +1 -1
  27. package/src/components/resources/resource-utils-karpenter.ts +0 -6
  28. package/src/components/resources/resource-utils.ts +108 -0
  29. package/src/components/timeline/shared.tsx +5 -6
  30. package/src/components/ui/Tooltip.tsx +73 -40
  31. package/src/components/ui/tooltip-position.test.ts +132 -0
  32. package/src/components/ui/tooltip-position.ts +86 -0
  33. package/src/components/workload/WorkloadView.tsx +6 -6
  34. package/src/utils/badge-colors.ts +19 -0
  35. package/src/utils/index.ts +2 -0
  36. package/src/utils/validators.test.ts +207 -0
  37. package/src/utils/validators.ts +178 -0
  38. package/src/utils/view-transition.test.ts +81 -0
  39. package/src/utils/view-transition.ts +62 -0
@@ -0,0 +1,81 @@
1
+ import { describe, it, expect, vi } from 'vitest'
2
+ import { isInvalidStateError, startViewTransitionSafe } from './view-transition'
3
+
4
+ // SKY-833 bug 49: rapid tab / route switches surfaced
5
+ // "InvalidStateError: Transition was aborted because of invalid state"
6
+ // as a console exception. The View Transitions API rejects the
7
+ // `finished` promise with that error whenever a new transition
8
+ // supersedes an in-flight one — which is the user's intent (they
9
+ // clicked again), not a bug. These tests pin the rejection-
10
+ // classification helper and the fallback path that's used when the
11
+ // API isn't available (Firefox, older Safari, jsdom).
12
+
13
+ describe('isInvalidStateError', () => {
14
+ it('returns true for objects whose name is "InvalidStateError"', () => {
15
+ expect(isInvalidStateError(new DOMException('boom', 'InvalidStateError'))).toBe(true)
16
+ expect(isInvalidStateError({ name: 'InvalidStateError', message: 'x' })).toBe(true)
17
+ })
18
+
19
+ it('returns false for any other shape', () => {
20
+ expect(isInvalidStateError(new Error('boom'))).toBe(false)
21
+ expect(isInvalidStateError({ name: 'TypeError' })).toBe(false)
22
+ expect(isInvalidStateError({})).toBe(false)
23
+ expect(isInvalidStateError('InvalidStateError')).toBe(false)
24
+ expect(isInvalidStateError(null)).toBe(false)
25
+ expect(isInvalidStateError(undefined)).toBe(false)
26
+ })
27
+ })
28
+
29
+ describe('startViewTransitionSafe', () => {
30
+ it('falls back to invoking update directly when the API is missing', () => {
31
+ const originalDoc = (globalThis as { document?: unknown }).document
32
+ ;(globalThis as { document: object }).document = {} // no startViewTransition
33
+
34
+ const update = vi.fn()
35
+ startViewTransitionSafe(update)
36
+ expect(update).toHaveBeenCalledTimes(1)
37
+
38
+ ;(globalThis as { document?: unknown }).document = originalDoc
39
+ })
40
+
41
+ it('uses the API when available and swallows InvalidStateError on the finished promise', async () => {
42
+ const originalDoc = (globalThis as { document?: unknown }).document
43
+
44
+ const update = vi.fn()
45
+ const startSpy = vi.fn((cb: () => void) => {
46
+ void cb // the real API invokes this; test doesn't need to
47
+ return {
48
+ finished: Promise.reject(new DOMException('superseded', 'InvalidStateError')),
49
+ ready: Promise.resolve(),
50
+ updateCallbackDone: Promise.resolve(),
51
+ skipTransition: () => {},
52
+ }
53
+ })
54
+ ;(globalThis as { document: object }).document = { startViewTransition: startSpy }
55
+
56
+ startViewTransitionSafe(update)
57
+ expect(startSpy).toHaveBeenCalledTimes(1)
58
+ // Argument is the update callback (the API itself invokes it).
59
+ expect(startSpy).toHaveBeenCalledWith(update)
60
+
61
+ // The unhandled rejection would surface here if we hadn't caught
62
+ // it. Awaiting a microtask yield is enough to flush the promise
63
+ // chain inside startViewTransitionSafe.
64
+ await Promise.resolve()
65
+ await Promise.resolve()
66
+
67
+ ;(globalThis as { document?: unknown }).document = originalDoc
68
+ })
69
+
70
+ it('only swallows InvalidStateError (verified via the classifier)', () => {
71
+ // Rather than triggering an actual unhandled rejection (which
72
+ // vitest treats as a test failure), pin the contract that
73
+ // startViewTransitionSafe relies on: InvalidStateError is the
74
+ // single name we suppress. Anything else returns false from the
75
+ // classifier and therefore propagates through the promise chain.
76
+ expect(isInvalidStateError(new DOMException('x', 'InvalidStateError'))).toBe(true)
77
+ expect(isInvalidStateError(new Error('something else broke'))).toBe(false)
78
+ expect(isInvalidStateError(new DOMException('x', 'AbortError'))).toBe(false)
79
+ expect(isInvalidStateError(new TypeError('y'))).toBe(false)
80
+ })
81
+ })
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Wrapper around `document.startViewTransition` that:
3
+ *
4
+ * 1. Falls back to a synchronous call to `update` when the View
5
+ * Transitions API isn't available (older Safari, Firefox without
6
+ * the flag, jsdom in tests).
7
+ * 2. Swallows the `InvalidStateError` that the API rejects with
8
+ * whenever a new transition starts while a previous one is still
9
+ * pending. This is a normal symptom of impatient users (clicking
10
+ * tabs faster than 200ms) — the new transition has already
11
+ * replaced the in-flight one, so there is nothing for the caller
12
+ * to do. SKY-833 bug 49 surfaced this as a console exception
13
+ * stack on rapid Secrets / drawer-tab navigation.
14
+ *
15
+ * Other rejection types (programming errors, unsupported configs)
16
+ * are re-thrown so we don't accidentally hide real bugs.
17
+ */
18
+ export function startViewTransitionSafe(update: () => void): void {
19
+ // Some lib.dom.d.ts versions don't yet expose `startViewTransition`
20
+ // on Document. Narrow once via a typed cast.
21
+ const doc = document as Document & {
22
+ startViewTransition?: (cb: () => void) => {
23
+ finished: Promise<void>
24
+ ready: Promise<void>
25
+ updateCallbackDone: Promise<void>
26
+ skipTransition: () => void
27
+ }
28
+ }
29
+
30
+ if (typeof doc.startViewTransition !== 'function') {
31
+ update()
32
+ return
33
+ }
34
+
35
+ const transition = doc.startViewTransition(update)
36
+
37
+ // The `finished` promise rejects with InvalidStateError when a new
38
+ // transition supersedes this one. That's not an error condition for
39
+ // the user — they got what they wanted (the new state). Swallow it
40
+ // so it doesn't become a noisy console exception.
41
+ transition.finished.catch((err: unknown) => {
42
+ if (isInvalidStateError(err)) return
43
+ // Anything else really is unexpected — let it surface.
44
+ throw err
45
+ })
46
+ }
47
+
48
+ /**
49
+ * Predicate that classifies the rejection thrown by the View
50
+ * Transitions API when a new transition supersedes an in-flight one.
51
+ * Used by `startViewTransitionSafe` to know what to swallow, and
52
+ * exported so tests (and any future call site that wraps the API
53
+ * differently) can pin the exact name we treat as expected.
54
+ */
55
+ export function isInvalidStateError(err: unknown): boolean {
56
+ return (
57
+ !!err &&
58
+ typeof err === 'object' &&
59
+ 'name' in err &&
60
+ (err as { name: unknown }).name === 'InvalidStateError'
61
+ )
62
+ }