diff --git a/frontend-modern/src/features/docker/DockerContainersTable.tsx b/frontend-modern/src/features/docker/DockerContainersTable.tsx index 2d9fa9c6f4..d8829ae216 100644 --- a/frontend-modern/src/features/docker/DockerContainersTable.tsx +++ b/frontend-modern/src/features/docker/DockerContainersTable.tsx @@ -251,8 +251,18 @@ export const DockerContainersTable: Component = (props) } return kinds.size > 1; }); + const showRestartColumn = createMemo(() => + scopedRows().some( + (resource) => + typeof resource.docker?.restartCount === 'number' && resource.docker.restartCount > 0, + ), + ); const visibleColumns = createMemo(() => - getDockerContainerVisibleColumnsForLayout(layoutMode(), showRuntimeColumn()), + getDockerContainerVisibleColumnsForLayout( + layoutMode(), + showRuntimeColumn(), + showRestartColumn(), + ), ); const visibleColumnIds = createMemo(() => visibleColumns().map((column) => column.id)); const drawerColSpan = createMemo(() => visibleColumns().length); @@ -335,7 +345,6 @@ export const DockerContainersTable: Component = (props) const indicator = mapDockerContainerStatus(resource); const image = () => dockerTextValue(resource.docker?.image); const state = () => containerState(resource); - const health = () => dockerTextValue(resource.docker?.health); const runtime = () => runtimeSummary(resource); const host = () => dockerHostName(resource); const running = () => isContainerRunning(resource); @@ -402,16 +411,6 @@ export const DockerContainersTable: Component = (props) ); - case 'health': - return ( - - - {health()} - - - ); case 'cpu': return ( diff --git a/frontend-modern/src/features/docker/__tests__/DockerNativeTables.test.tsx b/frontend-modern/src/features/docker/__tests__/DockerNativeTables.test.tsx index d007b63b60..1ed09c83a4 100644 --- a/frontend-modern/src/features/docker/__tests__/DockerNativeTables.test.tsx +++ b/frontend-modern/src/features/docker/__tests__/DockerNativeTables.test.tsx @@ -170,12 +170,12 @@ describe('Docker native tables', () => { expect(screen.getByText('Memory')).toBeInTheDocument(); expect(screen.getByText('Restarts')).toBeInTheDocument(); expect(screen.getByText('Updates')).toBeInTheDocument(); + expect(screen.queryByText('Health')).not.toBeInTheDocument(); expect(screen.getByText('edge-web')).toBeInTheDocument(); expect(screen.getByText('edge-01')).toBeInTheDocument(); expect(screen.getByText('docker 27.5.1')).toBeInTheDocument(); expect(screen.getByText('podman 5.2.1')).toBeInTheDocument(); expect(screen.getByText('nginx:latest')).toBeInTheDocument(); - expect(screen.getAllByText('healthy').length).toBe(2); expect(screen.getByText('2')).toBeInTheDocument(); expect(screen.getByText('0.0.0.0:8080->80/tcp')).toBeInTheDocument(); expect(screen.getByText('frontend 172.18.0.2')).toBeInTheDocument(); @@ -236,6 +236,46 @@ describe('Docker native tables', () => { expect(screen.queryByText('docker 27.5.1')).not.toBeInTheDocument(); }); + it('hides the Restarts column when the current containers have no restart signal', () => { + setViewportWidth(1500); + + renderInRouter(() => ( + } + emptyTitle="No containers" + emptyDescription="No containers" + showToolbar={false} + /> + )); + + expect(screen.queryByText('Restarts')).not.toBeInTheDocument(); + }); + it('renders actionable Docker container updates with native agent and container IDs', async () => { renderInRouter(() => ( { it('keeps the mobile container table on identity, state, live metrics, and update action', () => { - const columns = getDockerContainerVisibleColumnsForLayout('mobile', true); + const columns = getDockerContainerVisibleColumnsForLayout('mobile', true, true); const ids = columns.map((column) => column.id); expect(ids).toEqual(['container', 'state', 'cpu', 'memory', 'updates']); @@ -21,14 +21,26 @@ describe('dockerContainerTableModel', () => { }); }); - it('adds host and restart signal before slower forensic fields on tablet', () => { + it('adds host before slower forensic fields on tablet', () => { expect( - getDockerContainerVisibleColumnsForLayout('tablet', true).map((column) => column.id), - ).toEqual(['container', 'host', 'state', 'cpu', 'memory', 'restarts', 'updates']); + getDockerContainerVisibleColumnsForLayout('tablet', true, false).map((column) => column.id), + ).toEqual(['container', 'host', 'state', 'cpu', 'memory', 'updates']); + }); + + it('adds restarts only when the current row set has restart signal to scan', () => { + const withoutRestarts = getDockerContainerVisibleColumnsForLayout('compact', true, false).map( + (column) => column.id, + ); + const withRestarts = getDockerContainerVisibleColumnsForLayout('compact', true, true).map( + (column) => column.id, + ); + + expect(withoutRestarts).not.toContain('restarts'); + expect(withRestarts).toContain('restarts'); }); it('keeps compact desktop scan-focused and hides wide forensic columns', () => { - const columns = getDockerContainerVisibleColumnsForLayout('compact', true); + const columns = getDockerContainerVisibleColumnsForLayout('compact', true, true); const ids = columns.map((column) => column.id); expect(ids).toEqual([ @@ -49,10 +61,10 @@ describe('dockerContainerTableModel', () => { }); it('shows runtime only for mixed Docker and Podman fleets', () => { - const compactIds = getDockerContainerVisibleColumnsForLayout('compact', false).map( + const compactIds = getDockerContainerVisibleColumnsForLayout('compact', false, true).map( (column) => column.id, ); - const wideIds = getDockerContainerVisibleColumnsForLayout('wide', false).map( + const wideIds = getDockerContainerVisibleColumnsForLayout('wide', false, true).map( (column) => column.id, ); diff --git a/frontend-modern/src/features/docker/dockerContainerTableModel.ts b/frontend-modern/src/features/docker/dockerContainerTableModel.ts index c89a04a913..1e59a2a0dd 100644 --- a/frontend-modern/src/features/docker/dockerContainerTableModel.ts +++ b/frontend-modern/src/features/docker/dockerContainerTableModel.ts @@ -9,7 +9,6 @@ export type DockerContainerTableColumnId = | 'runtime' | 'image' | 'state' - | 'health' | 'cpu' | 'memory' | 'restarts' @@ -45,7 +44,6 @@ const DOCKER_CONTAINER_COLUMN_MIN_LAYOUT: Record< image: 'compact', runtime: 'compact', ports: 'compact', - health: 'wide', networks: 'wide', mounts: 'wide', }; @@ -56,7 +54,6 @@ const DOCKER_CONTAINER_COLUMNS: DockerContainerTableColumn[] = [ { id: 'runtime', label: 'Runtime', kind: 'text' }, { id: 'image', label: 'Image', kind: 'text' }, { id: 'state', label: 'State', kind: 'text' }, - { id: 'health', label: 'Health', kind: 'text' }, { id: 'cpu', label: 'CPU', kind: 'metric-bar' }, { id: 'memory', label: 'Memory', kind: 'metric-bar' }, { id: 'restarts', label: 'Restarts', kind: 'numeric-value' }, @@ -72,7 +69,6 @@ const DOCKER_CONTAINER_DESKTOP_WIDTHS: Record `${Number(value.toFixed(4))} export const getDockerContainerVisibleColumnsForLayout = ( layoutMode: WorkloadTableLayoutMode, includeRuntime: boolean, + includeRestarts: boolean, ): DockerContainerTableColumn[] => { const layoutRank = DOCKER_CONTAINER_TABLE_LAYOUT_ORDER[layoutMode]; return DOCKER_CONTAINER_COLUMNS.filter((column) => { if (column.id === 'runtime' && !includeRuntime) return false; + if (column.id === 'restarts' && !includeRestarts) return false; return ( DOCKER_CONTAINER_TABLE_LAYOUT_ORDER[DOCKER_CONTAINER_COLUMN_MIN_LAYOUT[column.id]] <= layoutRank