Files
grafana-zabbix/src/panel-triggers/components/Problems/Problems.test.tsx
Copilot 984f065296 Fix column visibility toggles not working in problem panel (#2228)
## Fix column visibility in problem panel

- [x] Understand the issue: Column IDs in
`initialState.columnVisibility` don't match actual column accessor IDs
- [x] Fix the column ID mapping in Problems.tsx
- [x] Add tests to verify column visibility works correctly
- [x] Fix reactivity issue: Moved columnVisibility from initialState to
state with useMemo
- [x] Add datasource column definition and visibility mapping
- [x] Optimize datasource cell rendering
- [x] Suppress React Compiler warning for useReactTable
- [x] All tests passing (9/9)
- [x] Build successful
- [x] Lint warnings reduced from 65 to 64

## Summary

Fixed three issues with column visibility in the problem panel:

1. **Incorrect column ID mappings**: Fixed three column IDs to match
actual column accessor IDs.
2. **Non-reactive column visibility**: Moved `columnVisibility` from
`initialState` to `state` with `useMemo`.
3. **Missing datasource column**: Added datasource column definition and
visibility mapping.
4. **Lint warning**: Suppressed React Compiler warning for
`useReactTable` hook (known TanStack Table issue).

All column visibility controls now correctly show/hide their respective
columns without requiring a page refresh.

<!-- START COPILOT ORIGINAL PROMPT -->



<details>

<summary>Original prompt</summary>

> 
> ----
> 
> *This section details on the original issue you should resolve*
> 
> <issue_title>Change column in problem panel not working on
6.1.1</issue_title>
> <issue_description>**Describe the bug**
> Since the upgrade from plugin 6.0.3 to 6.1.1, i notice that status
column appears (it was not the case before the upgrade) on existing
problem panels.
> I tried to disable it, and the button was on disable state. The
configuration is great, but not the display. Then i tried to disable or
enable all the colums and it doesn't work too.
> I tried on a new dashboard with a new problem panel and it is the same
issue.
> Font size doesn't work too. All other options seems to work.
> Grafana server has been restarted.
> 
> **Expected behavior**
> Enable or disable the column should change the display of the problem
panel.
> 
> **Screenshots**
> <img width="1916" height="833" alt="Image"
src="https://github.com/user-attachments/assets/1e725aff-fd2d-41cd-bd2b-855ac8c611d0"
/>
> 
> **Network data**
> If it's related to metric data visualization would be great to get the
raw query and response for the network request (check this in browser
dev tools network tab, there you can see metric requests, please include
the request body and request response)
> 
> **Software versions**
> 
> | Grafana | Zabbix | Grafana-Zabbix Plugin |
> | ------- | ------ | --------------------- |
> | 12.3.0   | 7.2.15  | 6.1.1                 |
> </issue_description>
> 
> <agent_instructions>Looks like the regression was introduced via
https://github.com/grafana/grafana-zabbix/pull/2131/changes#diff-85e7fa6e5295bf97f8bf82eabcb5807d695248b9fcd325acd58a8af02824cb70L207</agent_instructions>
> 
> ## Comments on the Issue (you are @copilot in this section)
> 
> <comments>
> <comment_new><author>@yesoreyeram</author><body>
> I can reproduce this issue. Adding to the backlog for further
investigation.</body></comment_new>
> </comments>
> 


</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

- Fixes grafana/grafana-zabbix#2213

<!-- START COPILOT CODING AGENT TIPS -->
---

 Let Copilot coding agent [set things up for
you](https://github.com/grafana/grafana-zabbix/issues/new?title=+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot)
— coding agent works faster and does higher quality work when set up for
your repo.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: yesoreyeram <153843+yesoreyeram@users.noreply.github.com>
Co-authored-by: Jocelyn Collado-Kuri <jcolladokuri@icloud.com>
Co-authored-by: jcolladokuri <20448042+jcolladokuri@users.noreply.github.com>
2026-01-26 15:58:40 +00:00

254 lines
8.1 KiB
TypeScript

import React from 'react';
import { render, screen, within } from '@testing-library/react';
import { ProblemList, ProblemListProps } from './Problems';
import { ProblemDTO, ZBXAlert, ZBXEvent } from '../../../datasource/types';
import { ProblemsPanelOptions, DEFAULT_SEVERITY } from '../../types';
import { APIExecuteScriptResponse, ZBXScript } from '../../../datasource/zabbix/connectors/zabbix_api/types';
// Mock @grafana/runtime
jest.mock('@grafana/runtime', () => ({
...jest.requireActual('@grafana/runtime'),
reportInteraction: jest.fn(),
config: {},
}));
describe('ProblemList', () => {
const mockGetProblemEvents = jest.fn<Promise<ZBXEvent[]>, [ProblemDTO]>();
const mockGetProblemAlerts = jest.fn<Promise<ZBXAlert[]>, [ProblemDTO]>();
const mockGetScripts = jest.fn<Promise<ZBXScript[]>, [ProblemDTO]>();
const mockOnExecuteScript = jest.fn<Promise<APIExecuteScriptResponse>, [ProblemDTO, string, string]>();
const mockOnProblemAck = jest.fn();
const mockOnTagClick = jest.fn();
const mockOnPageSizeChange = jest.fn();
const mockOnColumnResize = jest.fn();
const defaultPanelOptions: ProblemsPanelOptions = {
datasources: [],
fontSize: '100%',
layout: 'table',
schemaVersion: 1,
targets: [],
hostField: true,
hostTechNameField: false,
hostGroups: false,
hostProxy: false,
severityField: true,
statusField: true,
statusIcon: true,
opdataField: false,
ackField: true,
showTags: true,
showDatasourceName: false,
ageField: true,
customLastChangeFormat: false,
lastChangeFormat: '',
highlightNewEvents: false,
highlightNewerThan: '',
markAckEvents: false,
ackEventColor: 'rgb(56, 219, 156)',
okEventColor: 'rgb(56, 189, 113)',
triggerSeverity: DEFAULT_SEVERITY,
problemTimeline: false,
allowDangerousHTML: false,
resizedColumns: [],
};
const createMockProblem = (id: string, timestamp: number): ProblemDTO => ({
eventid: id,
name: `Test Problem ${id}`,
acknowledged: '0',
value: '1',
severity: '3',
priority: '3',
host: `Test Host ${id}`,
hostTechName: `host-${id}`,
hostInMaintenance: false,
groups: [],
proxy: '',
tags: [],
url: '',
opdata: '',
datasource: { type: 'alexanderzobnin-zabbix-datasource', uid: 'test-ds' },
timestamp,
acknowledges: [],
suppressed: '0',
suppression_data: [],
comments: '',
});
const defaultProps: ProblemListProps = {
problems: [],
panelOptions: defaultPanelOptions,
loading: false,
pageSize: 10,
fontSize: 100,
panelId: 1,
getProblemEvents: mockGetProblemEvents,
getProblemAlerts: mockGetProblemAlerts,
getScripts: mockGetScripts,
onExecuteScript: mockOnExecuteScript,
onProblemAck: mockOnProblemAck,
onTagClick: mockOnTagClick,
onPageSizeChange: mockOnPageSizeChange,
onColumnResize: mockOnColumnResize,
};
beforeEach(() => {
jest.clearAllMocks();
});
describe('Age Field', () => {
it('should render the age column header when ageField is enabled', () => {
const props = {
...defaultProps,
panelOptions: { ...defaultPanelOptions, ageField: true },
problems: [createMockProblem('1', 1609459200)], // 2021-01-01 00:00:00 UTC
};
render(<ProblemList {...props} />);
const table = screen.getByRole('table');
const headers = within(table).getAllByRole('columnheader');
const ageHeader = headers.find((header) => header.textContent === 'Age');
expect(ageHeader).toBeInTheDocument();
});
it('should not render the age column header when ageField is disabled', () => {
const props = {
...defaultProps,
panelOptions: { ...defaultPanelOptions, ageField: false },
problems: [createMockProblem('1', 1609459200)],
};
render(<ProblemList {...props} />);
const table = screen.getByRole('table');
const headers = within(table).getAllByRole('columnheader');
const ageHeader = headers.find((header) => header.textContent === 'Age');
expect(ageHeader).toBeUndefined();
});
});
describe('Status Field', () => {
it('should render the status column header when statusField is enabled', () => {
const props = {
...defaultProps,
panelOptions: { ...defaultPanelOptions, statusField: true },
problems: [createMockProblem('1', 1609459200)],
};
render(<ProblemList {...props} />);
const table = screen.getByRole('table');
const headers = within(table).getAllByRole('columnheader');
const statusHeader = headers.find((header) => header.textContent === 'Status');
expect(statusHeader).toBeInTheDocument();
});
it('should not render the status column header when statusField is disabled', () => {
const props = {
...defaultProps,
panelOptions: { ...defaultPanelOptions, statusField: false },
problems: [createMockProblem('1', 1609459200)],
};
render(<ProblemList {...props} />);
const table = screen.getByRole('table');
const headers = within(table).getAllByRole('columnheader');
const statusHeader = headers.find((header) => header.textContent === 'Status');
expect(statusHeader).toBeUndefined();
});
});
describe('Severity Field', () => {
it('should render the severity column header when severityField is enabled', () => {
const props = {
...defaultProps,
panelOptions: { ...defaultPanelOptions, severityField: true },
problems: [createMockProblem('1', 1609459200)],
};
render(<ProblemList {...props} />);
const table = screen.getByRole('table');
const headers = within(table).getAllByRole('columnheader');
const severityHeader = headers.find((header) => header.textContent === 'Severity');
expect(severityHeader).toBeInTheDocument();
});
it('should not render the severity column header when severityField is disabled', () => {
const props = {
...defaultProps,
panelOptions: { ...defaultPanelOptions, severityField: false },
problems: [createMockProblem('1', 1609459200)],
};
render(<ProblemList {...props} />);
const table = screen.getByRole('table');
const headers = within(table).getAllByRole('columnheader');
const severityHeader = headers.find((header) => header.textContent === 'Severity');
expect(severityHeader).toBeUndefined();
});
});
describe('Ack Field', () => {
it('should render the ack column header when ackField is enabled', () => {
const props = {
...defaultProps,
panelOptions: { ...defaultPanelOptions, ackField: true },
problems: [createMockProblem('1', 1609459200)],
};
render(<ProblemList {...props} />);
const table = screen.getByRole('table');
const headers = within(table).getAllByRole('columnheader');
const ackHeader = headers.find((header) => header.textContent === 'Ack');
expect(ackHeader).toBeInTheDocument();
});
it('should not render the ack column header when ackField is disabled', () => {
const props = {
...defaultProps,
panelOptions: { ...defaultPanelOptions, ackField: false },
problems: [createMockProblem('1', 1609459200)],
};
render(<ProblemList {...props} />);
const table = screen.getByRole('table');
const headers = within(table).getAllByRole('columnheader');
const ackHeader = headers.find((header) => header.textContent === 'Ack');
expect(ackHeader).toBeUndefined();
});
});
describe('Datasource Field', () => {
it('should not render the datasource column header when showDatasourceName is disabled', () => {
const props = {
...defaultProps,
panelOptions: { ...defaultPanelOptions, showDatasourceName: false },
problems: [createMockProblem('1', 1609459200)],
};
render(<ProblemList {...props} />);
const table = screen.getByRole('table');
const headers = within(table).getAllByRole('columnheader');
const datasourceHeader = headers.find((header) => header.textContent === 'Datasource');
expect(datasourceHeader).toBeUndefined();
});
});
});