diff --git a/src/panel-triggers/components/Problems/Problems.test.tsx b/src/panel-triggers/components/Problems/Problems.test.tsx
index 56aad0a..49a04d7 100644
--- a/src/panel-triggers/components/Problems/Problems.test.tsx
+++ b/src/panel-triggers/components/Problems/Problems.test.tsx
@@ -33,10 +33,12 @@ describe('ProblemList', () => {
hostGroups: false,
hostProxy: false,
severityField: true,
+ statusField: true,
statusIcon: true,
opdataField: false,
ackField: true,
showTags: true,
+ showDatasourceName: false,
ageField: true,
customLastChangeFormat: false,
lastChangeFormat: '',
@@ -111,5 +113,141 @@ describe('ProblemList', () => {
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();
+
+ 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();
+
+ 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();
+
+ 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();
+
+ 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();
+
+ 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();
+
+ 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();
+
+ 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();
+
+ const table = screen.getByRole('table');
+ const headers = within(table).getAllByRole('columnheader');
+ const datasourceHeader = headers.find((header) => header.textContent === 'Datasource');
+
+ expect(datasourceHeader).toBeUndefined();
+ });
});
});
diff --git a/src/panel-triggers/components/Problems/Problems.tsx b/src/panel-triggers/components/Problems/Problems.tsx
index c103db3..3ef77c8 100644
--- a/src/panel-triggers/components/Problems/Problems.tsx
+++ b/src/panel-triggers/components/Problems/Problems.tsx
@@ -23,7 +23,7 @@ import {
getPaginationRowModel,
useReactTable,
} from '@tanstack/react-table';
-import { reportInteraction } from '@grafana/runtime';
+import { getDataSourceSrv, reportInteraction } from '@grafana/runtime';
import { ProblemDetails } from './ProblemDetails';
import { capitalizeFirstLetter, parseCustomTagColumns } from './utils';
@@ -191,6 +191,19 @@ export const ProblemList = (props: ProblemListProps) => {
/>
),
}),
+ columnHelper.accessor('datasource', {
+ header: 'Datasource',
+ size: 120,
+ cell: ({ cell }) => {
+ const datasource = cell.getValue();
+ let dsName: string = datasource as string;
+ if ((datasource as DataSourceRef)?.uid) {
+ const dsInstance = getDataSourceSrv().getInstanceSettings((datasource as DataSourceRef).uid);
+ dsName = dsInstance?.name || dsName;
+ }
+ return {dsName};
+ },
+ }),
columnHelper.accessor('timestamp', {
id: 'age',
header: 'Age',
@@ -268,6 +281,27 @@ export const ProblemList = (props: ProblemListProps) => {
}));
}, [effectivePageSize]);
+ // Column visibility state derived from panelOptions
+ const columnVisibility = useMemo(
+ () => ({
+ host: panelOptions.hostField,
+ hostTechName: panelOptions.hostTechNameField,
+ groups: panelOptions.hostGroups,
+ proxy: panelOptions.hostProxy,
+ priority: panelOptions.severityField,
+ statusIcon: panelOptions.statusIcon,
+ value: panelOptions.statusField,
+ opdata: panelOptions.opdataField,
+ acknowledged: panelOptions.ackField,
+ tags: panelOptions.showTags,
+ datasource: panelOptions.showDatasourceName,
+ age: panelOptions.ageField,
+ }),
+ [panelOptions]
+ );
+
+ // https://github.com/TanStack/table/issues/6137
+ // eslint-disable-next-line react-hooks/incompatible-library -- TanStack Table's useReactTable returns functions that cannot be memoized
const table = useReactTable({
data: problems,
columns,
@@ -276,25 +310,12 @@ export const ProblemList = (props: ProblemListProps) => {
state: {
columnSizing,
pagination,
+ columnVisibility,
},
onPaginationChange: setPagination,
meta: {
panelOptions,
},
- initialState: {
- columnVisibility: {
- host: panelOptions.hostField,
- hostTechName: panelOptions.hostTechNameField,
- groups: panelOptions.hostGroups,
- proxy: panelOptions.hostProxy,
- severity: panelOptions.severityField,
- statusIcon: panelOptions.statusIcon,
- opdata: panelOptions.opdataField,
- ack: panelOptions.ackField,
- tags: panelOptions.showTags,
- age: panelOptions.ageField,
- },
- },
onColumnSizingChange: (updater) => {
const newSizing = typeof updater === 'function' ? updater(columnSizing) : updater;
setColumnSizing(newSizing);