mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-23 20:25:56 +00:00
Merge remote-tracking branch 'origin/main' into fix/workloads-backup-pill-width
This commit is contained in:
@@ -1215,7 +1215,10 @@ render through `KubernetesNameText`, which splits off the trailing segment
|
||||
part that tells siblings apart (`checkout-api-6d…-x7k2p`, `prod…-03`), with the
|
||||
full name on the title. Pod names, the Pods table's Node column and controller
|
||||
names use it. Deployment names are chosen by people, so they keep plain end
|
||||
truncation.
|
||||
truncation. Head and tail are flex items, so a browser copy put a line break
|
||||
between them and a pasted `kubectl logs` argument broke; a copy whose selection
|
||||
sits inside one name now writes the name without the break, and wider
|
||||
selections such as a whole row copy as the browser builds them.
|
||||
With one cluster in view, every Kubernetes scope cell repeated the cluster
|
||||
name and crowded out the namespace that differs between rows, so the tables
|
||||
show the namespace alone (`Cluster` for cluster-scoped objects) through
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"version": 1,
|
||||
"base_sha": "834fcaeb366b2644bfa5c081e81bb2f286c34e55",
|
||||
"verified_at": "2026-10-05T15:03:48Z",
|
||||
"result": "passed",
|
||||
"changed_paths": [
|
||||
"frontend-modern/src/features/kubernetes/KubernetesNameText.tsx"
|
||||
],
|
||||
"content_sha256": {
|
||||
"frontend-modern/src/features/kubernetes/KubernetesNameText.tsx": "0d293feee3fc1f1bdda26000013b44199963a8054fb18990cfc7dc62363e03d1"
|
||||
},
|
||||
"routes": [
|
||||
"/kubernetes/pods"
|
||||
],
|
||||
"viewports": [
|
||||
{
|
||||
"width": 1440,
|
||||
"height": 900
|
||||
},
|
||||
{
|
||||
"width": 768,
|
||||
"height": 1024
|
||||
}
|
||||
],
|
||||
"states": [
|
||||
"Private mock-mode stack built from this tree (backend :7745, Vite :5265), headless Chromium via Playwright with clipboard read/write granted.",
|
||||
"Pods table Pod name cron-nightly-backfill-28918234, Pods table Node column prod-euw1-k8s-03 and Workload controllers name nightly-backfill-28918234 at both widths. At 768 px the pod and controller heads truncate (cron-ni\u2026 -28918234) and the tails stay visible, unchanged from before the fix.",
|
||||
"Before the fix (component swapped back via HMR) every one of the six copies put a newline between head and tail in the clipboard, e.g. cron-nightly-backfill\\n-28918234. After the fix all six clipboard reads equal the bare name.",
|
||||
"A whole-row selection still copies as the browser builds it (cells on separate lines), so wider selections are not rewritten. No page errors.",
|
||||
"Not verified: Firefox or Safari clipboard serialization, or a pasted kubectl command against a real cluster."
|
||||
],
|
||||
"interactions": [
|
||||
"Programmatic Range over each name wrapper, then a real ControlOrMeta+C keypress, then navigator.clipboard.readText().",
|
||||
"Real mouse drag from the first to the last character of each name, then ControlOrMeta+C and a clipboard read; selection.toString() still holds the newline while the clipboard does not.",
|
||||
"Range over the whole cron-nightly-backfill pod row, ControlOrMeta+C, clipboard read to confirm the handler leaves it alone."
|
||||
]
|
||||
}
|
||||
@@ -11,8 +11,23 @@ import { splitKubernetesNameTail } from './kubernetesPageModel';
|
||||
*/
|
||||
export const KubernetesNameText: Component<{ name: string; class?: string }> = (props) => {
|
||||
const parts = () => splitKubernetesNameTail(props.name);
|
||||
// The head and tail are flex items, so a plain copy inserts a line break
|
||||
// between them and a pasted `kubectl logs` breaks. A selection inside one
|
||||
// name copies without it; wider selections copy as the browser builds them.
|
||||
const copyWithoutBreak = (event: ClipboardEvent & { currentTarget: HTMLSpanElement }) => {
|
||||
const selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0 || !event.clipboardData) return;
|
||||
if (!event.currentTarget.contains(selection.getRangeAt(0).commonAncestorContainer)) return;
|
||||
event.clipboardData.setData('text/plain', selection.toString().replace(/\r?\n/g, ''));
|
||||
event.preventDefault();
|
||||
};
|
||||
return (
|
||||
<span class={`flex min-w-0 ${props.class ?? ''}`} title={props.name} data-kubernetes-name>
|
||||
<span
|
||||
class={`flex min-w-0 ${props.class ?? ''}`}
|
||||
title={props.name}
|
||||
data-kubernetes-name
|
||||
onCopy={copyWithoutBreak}
|
||||
>
|
||||
<span class="truncate">{parts().head}</span>
|
||||
<span class="max-w-full shrink-0 truncate" data-kubernetes-name-tail>
|
||||
{parts().tail}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { cleanup, render, screen } from '@solidjs/testing-library';
|
||||
import { afterEach, describe, expect, it } from 'vitest';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import type { Resource } from '@/types/resource';
|
||||
import { KubernetesPodsTable } from '../KubernetesPodsTable';
|
||||
@@ -152,6 +152,52 @@ describe('KubernetesPodsTable', () => {
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it('copies a generated name without a break between its head and tail', () => {
|
||||
const { container } = render(() => (
|
||||
<KubernetesPodsTable
|
||||
resources={[
|
||||
makeResource({
|
||||
id: 'cron-nightly-backfill-28918234',
|
||||
kubernetes: {
|
||||
clusterId: 'prod-euw1',
|
||||
namespace: 'batch',
|
||||
nodeName: 'prod-euw1-k8s-02',
|
||||
podName: 'cron-nightly-backfill-28918234',
|
||||
podPhase: 'Running',
|
||||
},
|
||||
}),
|
||||
]}
|
||||
emptyIcon={<span />}
|
||||
emptyTitle="No pods"
|
||||
emptyDescription="No pods"
|
||||
showToolbar={false}
|
||||
/>
|
||||
));
|
||||
|
||||
// Head and tail are flex items, so a browser copy would put a line break
|
||||
// between them; a selection inside one name copies as the name.
|
||||
const podName = screen.getByText(kubernetesName('cron-nightly-backfill-28918234'));
|
||||
expect(podName.querySelector('[data-kubernetes-name-tail]')).toHaveTextContent('-28918234');
|
||||
const copy = (selected: Node) => {
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(selected);
|
||||
window.getSelection()?.removeAllRanges();
|
||||
window.getSelection()?.addRange(range);
|
||||
const setData = vi.fn();
|
||||
const event = new Event('copy', { bubbles: true, cancelable: true });
|
||||
Object.defineProperty(event, 'clipboardData', { value: { setData } });
|
||||
podName.dispatchEvent(event);
|
||||
return { setData, prevented: event.defaultPrevented };
|
||||
};
|
||||
const inside = copy(podName);
|
||||
expect(inside.setData).toHaveBeenCalledWith('text/plain', 'cron-nightly-backfill-28918234');
|
||||
expect(inside.prevented).toBe(true);
|
||||
// A selection wider than the name (a whole row) is left to the browser.
|
||||
const wider = copy(container);
|
||||
expect(wider.setData).not.toHaveBeenCalled();
|
||||
expect(wider.prevented).toBe(false);
|
||||
});
|
||||
|
||||
it('renders pod rows with status mapped from podPhase + container readiness, attention rows first', () => {
|
||||
render(() => (
|
||||
<KubernetesPodsTable
|
||||
|
||||
Reference in New Issue
Block a user