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);